[evolution-data-server/account-mgmt: 9/42] Add new ESource classes.



commit ded01d3419bb00518c53cb716cb0adb7ebc2db3d
Author: Matthew Barnes <mbarnes redhat com>
Date:   Fri Nov 12 10:19:34 2010 -0500

    Add new ESource classes.
    
    ESource
    ESourceExtension
    ESourceRegistry

 addressbook/libebook/Makefile.am                   |    4 +-
 addressbook/libebook/e-book.c                      |    4 +
 calendar/libecal/Makefile.am                       |    6 +-
 calendar/libecal/e-cal.c                           |    6 +
 configure.ac                                       |    2 +-
 .../addressbook/libebook/libebook-docs.sgml        |    1 +
 .../addressbook/libebook/libebook-sections.txt     |   19 +
 docs/reference/addressbook/libebook/libebook.types |    2 +
 .../libebook/tmpl/e-source-address-book.sgml       |   35 +
 docs/reference/calendar/libecal/libecal-docs.sgml  |    3 +
 .../calendar/libecal/libecal-sections.txt          |  147 +
 docs/reference/calendar/libecal/libecal.types      |    4 +
 .../calendar/libecal/tmpl/e-source-calendar.sgml   |   35 +
 .../calendar/libecal/tmpl/e-source-memo-list.sgml  |   35 +
 .../calendar/libecal/tmpl/e-source-memo_list.sgml  |   35 +
 .../calendar/libecal/tmpl/e-source-task-list.sgml  |   35 +
 .../calendar/libecal/tmpl/e-source-task_list.sgml  |   35 +
 docs/reference/libedataserver/Makefile.am          |    2 +
 .../libedataserver/libedataserver-docs.sgml        |   35 +-
 .../libedataserver/libedataserver-sections.txt     |  751 ++++-
 docs/reference/libedataserver/libedataserver.types |   50 +
 .../libedataserverui/libedataserverui-sections.txt |    4 +-
 libebackend/e-source-registry-server.c             |    8 +-
 libedataserver/Makefile.am                         |   69 +-
 libedataserver/e-marshal.list                      |    1 +
 libedataserver/e-source-address-book.c             |   59 +
 libedataserver/e-source-address-book.h             |   80 +
 libedataserver/e-source-alarms.c                   |  312 ++
 libedataserver/e-source-alarms.h                   |   90 +
 libedataserver/e-source-authentication.c           |  575 ++++
 libedataserver/e-source-authentication.h           |  109 +
 libedataserver/e-source-authenticator.c            |  496 +++
 libedataserver/e-source-authenticator.h            |  103 +
 libedataserver/e-source-autocomplete.c             |  168 +
 libedataserver/e-source-autocomplete.h             |   86 +
 libedataserver/e-source-backend.c                  |  226 ++
 libedataserver/e-source-backend.h                  |   77 +
 libedataserver/e-source-calendar.c                 |  135 +
 libedataserver/e-source-calendar.h                 |  185 +
 libedataserver/e-source-camel.c                    |  738 ++++
 libedataserver/e-source-camel.h                    |   84 +
 libedataserver/e-source-collection.c               |  369 ++
 libedataserver/e-source-collection.h               |  103 +
 libedataserver/e-source-enums.h                    |   62 +
 libedataserver/e-source-extension.c                |  190 ++
 libedataserver/e-source-extension.h                |   73 +
 libedataserver/e-source-goa.c                      |  239 ++
 libedataserver/e-source-goa.h                      |   85 +
 libedataserver/e-source-mail-account.c             |  225 ++
 libedataserver/e-source-mail-account.h             |   88 +
 libedataserver/e-source-mail-composition.c         |  653 ++++
 libedataserver/e-source-mail-composition.h         |  116 +
 libedataserver/e-source-mail-identity.c            |  722 ++++
 libedataserver/e-source-mail-identity.h            |  116 +
 libedataserver/e-source-mail-signature.c           |  971 ++++++
 libedataserver/e-source-mail-signature.h           |  143 +
 libedataserver/e-source-mail-submission.c          |  346 ++
 libedataserver/e-source-mail-submission.h          |   95 +
 libedataserver/e-source-mail-transport.c           |   62 +
 libedataserver/e-source-mail-transport.h           |   81 +
 libedataserver/e-source-mdn.c                      |  172 +
 libedataserver/e-source-mdn.h                      |   88 +
 libedataserver/e-source-offline.c                  |  168 +
 libedataserver/e-source-offline.h                  |   85 +
 libedataserver/e-source-openpgp.c                  |  561 +++
 libedataserver/e-source-openpgp.h                  |  107 +
 libedataserver/e-source-refresh.c                  |  629 ++++
 libedataserver/e-source-refresh.h                  |  103 +
 libedataserver/e-source-registry.c                 | 3561 ++++++++++++++++++++
 libedataserver/e-source-registry.h                 |  207 ++
 libedataserver/e-source-security.c                 |  327 ++
 libedataserver/e-source-security.h                 |   87 +
 libedataserver/e-source-selectable.c               |  301 ++
 libedataserver/e-source-selectable.h               |   79 +
 libedataserver/e-source-smime.c                    |  681 ++++
 libedataserver/e-source-smime.h                    |  117 +
 libedataserver/e-source-webdav.c                   | 1083 ++++++
 libedataserver/e-source-webdav.h                   |  120 +
 libedataserver/e-source.c                          | 2514 ++++++++++-----
 libedataserver/e-source.h                          |  169 +-
 libedataserver/e-system-source.c                   |  117 +
 libedataserver/e-system-source.h                   |   63 +
 libedataserver/libedataserver.pc.in                |    2 +-
 83 files changed, 19988 insertions(+), 943 deletions(-)
---
diff --git a/addressbook/libebook/Makefile.am b/addressbook/libebook/Makefile.am
index 801f8ed..74a8ea1 100644
--- a/addressbook/libebook/Makefile.am
+++ b/addressbook/libebook/Makefile.am
@@ -36,7 +36,8 @@ libebook_1_2_la_SOURCES =				\
 	e-contact.c					\
 	e-destination.c					\
 	e-name-western.c				\
-	e-name-western-tables.h                         \
+	e-name-western-tables.h				\
+	e-source-address-book.c				\
 	e-vcard.c					\
 	e-error.h
 
@@ -65,6 +66,7 @@ libebookinclude_HEADERS =				\
 	e-contact.h					\
 	e-destination.h					\
 	e-name-western.h				\
+	e-source-address-book.h				\
 	e-vcard.h
 
 %-$(API_VERSION).pc: %.pc
diff --git a/addressbook/libebook/e-book.c b/addressbook/libebook/e-book.c
index 7b51b1c..2a6e90f 100644
--- a/addressbook/libebook/e-book.c
+++ b/addressbook/libebook/e-book.c
@@ -44,6 +44,7 @@
 #include "e-error.h"
 #include "e-contact.h"
 #include "e-name-western.h"
+#include "e-source-address-book.h"
 #include "e-book-view-private.h"
 #include "e-book-marshal.h"
 
@@ -262,6 +263,9 @@ e_book_class_init (EBookClass *e_book_class)
 	gobject_class->finalize = e_book_finalize;
 
 	g_type_class_add_private (e_book_class, sizeof (EBookPrivate));
+
+	/* Register relevant ESource extensions. */
+	E_TYPE_SOURCE_ADDRESS_BOOK;
 }
 
 static void
diff --git a/calendar/libecal/Makefile.am b/calendar/libecal/Makefile.am
index 68e6e43..184c31f 100644
--- a/calendar/libecal/Makefile.am
+++ b/calendar/libecal/Makefile.am
@@ -36,7 +36,8 @@ libecal_1_2_la_SOURCES =			\
 	e-cal-system-timezone.h			\
 	e-cal-util.c				\
 	e-cal-view.c				\
-	e-cal-view-private.h
+	e-cal-view-private.h			\
+	e-source-calendar.c
 
 libecal_1_2_la_LIBADD = 					\
 	$(top_builddir)/libedataserver/libedataserver-1.2.la 	\
@@ -62,7 +63,8 @@ libecalinclude_HEADERS =	\
 	e-cal-system-timezone.h	\
 	e-cal-types.h		\
 	e-cal-util.h		\
-	e-cal-view.h
+	e-cal-view.h		\
+	e-source-calendar.h
 
 %-$(API_VERSION).pc: %.pc
 	 cp $< $@
diff --git a/calendar/libecal/e-cal.c b/calendar/libecal/e-cal.c
index cfddc67..c547bfd 100644
--- a/calendar/libecal/e-cal.c
+++ b/calendar/libecal/e-cal.c
@@ -52,6 +52,7 @@
 #include "e-cal-time-util.h"
 #include "e-cal-view-private.h"
 #include "e-cal.h"
+#include "e-source-calendar.h"
 
 #include "e-gdbus-cal.h"
 #include "e-gdbus-cal-view.h"
@@ -677,6 +678,11 @@ e_cal_class_init (ECalClass *class)
 	object_class->finalize = e_cal_finalize;
 
 	g_type_class_add_private (class, sizeof (ECalPrivate));
+
+	/* Register relevant ESource extensions. */
+	E_TYPE_SOURCE_CALENDAR;
+	E_TYPE_SOURCE_MEMO_LIST;
+	E_TYPE_SOURCE_TASK_LIST;
 }
 
 static void
diff --git a/configure.ac b/configure.ac
index e1f99b5..55bec93 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1313,7 +1313,7 @@ PKG_CHECK_MODULES(SQLITE3, [sqlite3 >= sqlite_minimum_version])
 dnl ******************************
 dnl libedataserver flags
 dnl ******************************
-E_DATA_SERVER_DEPS="gio-2.0 gmodule-2.0 libxml-2.0 libsoup-2.4 gconf-2.0 $mozilla_nspr"
+E_DATA_SERVER_DEPS="gio-2.0 gmodule-2.0 gnome-keyring-1 libxml-2.0 libsoup-2.4 gconf-2.0 $mozilla_nspr"
 
 EVO_SET_COMPILE_FLAGS(E_DATA_SERVER, $E_DATA_SERVER_DEPS, $MANUAL_NSPR_CFLAGS, $MANUAL_NSPR_LIBS)
 AC_SUBST(E_DATA_SERVER_CFLAGS)
diff --git a/docs/reference/addressbook/libebook/libebook-docs.sgml b/docs/reference/addressbook/libebook/libebook-docs.sgml
index 219d02b..2a0a7fb 100644
--- a/docs/reference/addressbook/libebook/libebook-docs.sgml
+++ b/docs/reference/addressbook/libebook/libebook-docs.sgml
@@ -19,6 +19,7 @@
     <xi:include href="xml/e-destination.xml"/>
     <xi:include href="xml/e-address-western.xml"/>
     <xi:include href="xml/e-name-western.xml"/>
+    <xi:include href="xml/e-source-address-book.xml"/>
   </chapter>
 
   <chapter>
diff --git a/docs/reference/addressbook/libebook/libebook-sections.txt b/docs/reference/addressbook/libebook/libebook-sections.txt
index ede2423..19bce3a 100644
--- a/docs/reference/addressbook/libebook/libebook-sections.txt
+++ b/docs/reference/addressbook/libebook/libebook-sections.txt
@@ -532,3 +532,22 @@ e_destination_get_type
 <SUBSECTION Private>
 EDestinationPrivate
 </SECTION>
+
+<SECTION>
+<FILE>e-source-address-book</FILE>
+<TITLE>ESourceAddressBook</TITLE>
+ESourceAddressBook
+E_SOURCE_EXTENSION_ADDRESS_BOOK
+<SUBSECTION Standard>
+E_SOURCE_ADDRESS_BOOK
+E_IS_SOURCE_ADDRESS_BOOK
+E_TYPE_SOURCE_ADDRESS_BOOK
+E_SOURCE_ADDRESS_BOOK_CLASS
+E_IS_SOURCE_ADDRESS_BOOK_CLASS
+E_SOURCE_ADDRESS_BOOK_GET_CLASS
+ESourceAddressBookClass
+<SUBSECTION Private>
+ESourceAddressBookPrivate
+e_source_address_book_get_type
+</SECTION>
+
diff --git a/docs/reference/addressbook/libebook/libebook.types b/docs/reference/addressbook/libebook/libebook.types
index a52ebb9..d7bec67 100644
--- a/docs/reference/addressbook/libebook/libebook.types
+++ b/docs/reference/addressbook/libebook/libebook.types
@@ -5,6 +5,7 @@
 #include <libebook/e-contact.h>
 #include <libebook/e-destination.h>
 #include <libebook/e-vcard.h>
+#include <libebook/e-source-address-book.h>
 
 e_book_get_type
 e_book_client_get_type
@@ -13,3 +14,4 @@ e_book_view_get_type
 e_contact_get_type
 e_destination_get_type
 e_vcard_get_type
+e_source_address_book_get_type
diff --git a/docs/reference/addressbook/libebook/tmpl/e-source-address-book.sgml b/docs/reference/addressbook/libebook/tmpl/e-source-address-book.sgml
new file mode 100644
index 0000000..8384390
--- /dev/null
+++ b/docs/reference/addressbook/libebook/tmpl/e-source-address-book.sgml
@@ -0,0 +1,35 @@
+<!-- ##### SECTION Title ##### -->
+ESourceAddressBook
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT ESourceAddressBook ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### MACRO E_SOURCE_EXTENSION_ADDRESS_BOOK ##### -->
+<para>
+
+</para>
+
+
+
diff --git a/docs/reference/calendar/libecal/libecal-docs.sgml b/docs/reference/calendar/libecal/libecal-docs.sgml
index bcf23c0..819cf0c 100644
--- a/docs/reference/calendar/libecal/libecal-docs.sgml
+++ b/docs/reference/calendar/libecal/libecal-docs.sgml
@@ -19,6 +19,9 @@
     <xi:include href="xml/e-cal-util.xml"/>
     <xi:include href="xml/e-cal-system-timezone.xml"/>
     <xi:include href="xml/e-cal-check-timezones.xml"/>
+    <xi:include href="xml/e-source-calendar.xml"/>
+    <xi:include href="xml/e-source-memo-list.xml"/>
+    <xi:include href="xml/e-source-task-list.xml"/>
   </chapter>
 
   <chapter>
diff --git a/docs/reference/calendar/libecal/libecal-sections.txt b/docs/reference/calendar/libecal/libecal-sections.txt
index f84fe43..497e627 100644
--- a/docs/reference/calendar/libecal/libecal-sections.txt
+++ b/docs/reference/calendar/libecal/libecal-sections.txt
@@ -401,6 +401,92 @@ e_cal_component_get_type
 </SECTION>
 
 <SECTION>
+<FILE>e-cal</FILE>
+<TITLE>ECal</TITLE>
+ECal
+ECalSourceType
+ECalSetModeStatus
+ECalLoadState
+ECalAuthFunc
+e_cal_new
+e_cal_new_from_uri
+e_cal_new_system_calendar
+e_cal_new_system_tasks
+e_cal_new_system_memos
+e_cal_set_auth_func
+e_cal_open
+e_cal_open_async
+e_cal_refresh
+e_cal_remove
+e_cal_uri_list
+e_cal_get_source_type
+e_cal_get_load_state
+e_cal_get_source
+e_cal_get_uri
+e_cal_is_read_only
+e_cal_get_cal_address
+e_cal_get_alarm_email_address
+e_cal_get_ldap_attribute
+e_cal_get_one_alarm_only
+e_cal_get_organizer_must_attend
+e_cal_get_save_schedules
+e_cal_get_static_capability
+e_cal_get_organizer_must_accept
+e_cal_get_refresh_supported
+e_cal_set_mode
+e_cal_get_default_object
+e_cal_get_object
+e_cal_get_objects_for_uid
+e_cal_get_changes
+e_cal_free_change_list
+e_cal_get_object_list
+e_cal_get_object_list_as_comp
+e_cal_free_object_list
+e_cal_get_free_busy
+e_cal_generate_instances
+e_cal_generate_instances_for_object
+e_cal_get_alarms_in_range
+e_cal_free_alarms
+e_cal_get_alarms_for_object
+e_cal_create_object
+e_cal_modify_object
+e_cal_remove_object
+e_cal_remove_object_with_mod
+e_cal_discard_alarm
+e_cal_receive_objects
+e_cal_send_objects
+e_cal_get_timezone
+e_cal_add_timezone
+e_cal_set_default_timezone
+e_cal_get_query
+e_cal_resolve_tzid_cb
+e_cal_get_component_as_string
+e_cal_get_error_message
+e_cal_open_default
+e_cal_set_default
+e_cal_set_default_source
+e_cal_get_local_attachment_store
+e_cal_get_recurrences_no_master
+e_cal_get_attachments_for_comp
+<SUBSECTION Standard>
+E_CAL
+E_IS_CAL
+E_TYPE_CAL
+E_CAL_CLASS
+E_IS_CAL_CLASS
+E_TYPE_CAL_SOURCE_TYPE
+E_CAL_SET_MODE_STATUS_ENUM_TYPE
+CAL_MODE_ENUM_TYPE
+ECalClass
+<SUBSECTION Private>
+ECalPrivate
+e_cal_get_type
+e_cal_set_mode_status_enum_get_type
+e_cal_source_type_enum_get_type
+cal_mode_enum_get_type
+</SECTION>
+
+<SECTION>
 <FILE>e-cal-recur</FILE>
 ECalRecurInstanceFn
 ECalRecurResolveTimezoneFn
@@ -534,3 +620,64 @@ e_cal_view_get_type
 e_cal_view_new
 </SECTION>
 
+<SECTION>
+<FILE>e-cal-check-timezones</FILE>
+e_cal_check_timezones
+e_cal_tzlookup_ecal
+e_cal_tzlookup_icomp
+e_cal_match_tzid
+</SECTION>
+
+<SECTION>
+<FILE>e-source-calendar</FILE>
+<TITLE>ESourceCalendar</TITLE>
+ESourceCalendar
+E_SOURCE_EXTENSION_CALENDAR
+<SUBSECTION Standard>
+E_SOURCE_CALENDAR
+E_IS_SOURCE_CALENDAR
+E_TYPE_SOURCE_CALENDAR
+E_SOURCE_CALENDAR_CLASS
+E_IS_SOURCE_CALENDAR_CLASS
+E_SOURCE_CALENDAR_GET_CLASS
+ESourceCalendarClass
+<SUBSECTION Private>
+ESourceCalendarPrivate
+e_source_calendar_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-memo-list</FILE>
+<TITLE>ESourceMemoList</TITLE>
+ESourceMemoList
+E_SOURCE_EXTENSION_MEMO_LIST
+<SUBSECTION Standard>
+E_SOURCE_MEMO_LIST
+E_IS_SOURCE_MEMO_LIST
+E_TYPE_SOURCE_MEMO_LIST
+E_SOURCE_MEMO_LIST_CLASS
+E_IS_SOURCE_MEMO_LIST_CLASS
+E_SOURCE_MEMO_LIST_GET_CLASS
+ESourceMemoListClass
+<SUBSECTION Private>
+ESourceMemoListPrivate
+e_source_memo_list_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-task-list</FILE>
+<TITLE>ESourceTaskList</TITLE>
+ESourceTaskList
+E_SOURCE_EXTENSION_TASK_LIST
+<SUBSECTION Standard>
+E_SOURCE_TASK_LIST
+E_IS_SOURCE_TASK_LIST
+E_TYPE_SOURCE_TASK_LIST
+E_SOURCE_TASK_LIST_CLASS
+E_IS_SOURCE_TASK_LIST_CLASS
+E_SOURCE_TASK_LIST_GET_CLASS
+ESourceTaskListClass
+<SUBSECTION Private>
+ESourceTaskListPrivate
+e_source_task_list_get_type
+</SECTION>
diff --git a/docs/reference/calendar/libecal/libecal.types b/docs/reference/calendar/libecal/libecal.types
index 62b32cc..a1f72d9 100644
--- a/docs/reference/calendar/libecal/libecal.types
+++ b/docs/reference/calendar/libecal/libecal.types
@@ -3,9 +3,13 @@
 #include <libecal/e-cal-client-view.h>
 #include <libecal/e-cal-component.h>
 #include <libecal/e-cal-view.h>
+#include <libecal/e-source-calendar.h>
 
 e_cal_get_type
 e_cal_client_get_type
 e_cal_client_view_get_type
 e_cal_component_get_type
 e_cal_view_get_type
+e_source_calendar_get_type
+e_source_memo_list_get_type
+e_source_task_list_get_type
diff --git a/docs/reference/calendar/libecal/tmpl/e-source-calendar.sgml b/docs/reference/calendar/libecal/tmpl/e-source-calendar.sgml
new file mode 100644
index 0000000..b1aa2fc
--- /dev/null
+++ b/docs/reference/calendar/libecal/tmpl/e-source-calendar.sgml
@@ -0,0 +1,35 @@
+<!-- ##### SECTION Title ##### -->
+ESourceCalendar
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT ESourceCalendar ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### MACRO E_SOURCE_EXTENSION_CALENDAR ##### -->
+<para>
+
+</para>
+
+
+
diff --git a/docs/reference/calendar/libecal/tmpl/e-source-memo-list.sgml b/docs/reference/calendar/libecal/tmpl/e-source-memo-list.sgml
new file mode 100644
index 0000000..a582fa7
--- /dev/null
+++ b/docs/reference/calendar/libecal/tmpl/e-source-memo-list.sgml
@@ -0,0 +1,35 @@
+<!-- ##### SECTION Title ##### -->
+ESourceMemoList
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT ESourceMemoList ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### MACRO E_SOURCE_EXTENSION_MEMO_LIST ##### -->
+<para>
+
+</para>
+
+
+
diff --git a/docs/reference/calendar/libecal/tmpl/e-source-memo_list.sgml b/docs/reference/calendar/libecal/tmpl/e-source-memo_list.sgml
new file mode 100644
index 0000000..a582fa7
--- /dev/null
+++ b/docs/reference/calendar/libecal/tmpl/e-source-memo_list.sgml
@@ -0,0 +1,35 @@
+<!-- ##### SECTION Title ##### -->
+ESourceMemoList
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT ESourceMemoList ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### MACRO E_SOURCE_EXTENSION_MEMO_LIST ##### -->
+<para>
+
+</para>
+
+
+
diff --git a/docs/reference/calendar/libecal/tmpl/e-source-task-list.sgml b/docs/reference/calendar/libecal/tmpl/e-source-task-list.sgml
new file mode 100644
index 0000000..9cb620b
--- /dev/null
+++ b/docs/reference/calendar/libecal/tmpl/e-source-task-list.sgml
@@ -0,0 +1,35 @@
+<!-- ##### SECTION Title ##### -->
+ESourceTaskList
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT ESourceTaskList ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### MACRO E_SOURCE_EXTENSION_TASK_LIST ##### -->
+<para>
+
+</para>
+
+
+
diff --git a/docs/reference/calendar/libecal/tmpl/e-source-task_list.sgml b/docs/reference/calendar/libecal/tmpl/e-source-task_list.sgml
new file mode 100644
index 0000000..9cb620b
--- /dev/null
+++ b/docs/reference/calendar/libecal/tmpl/e-source-task_list.sgml
@@ -0,0 +1,35 @@
+<!-- ##### SECTION Title ##### -->
+ESourceTaskList
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT ESourceTaskList ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### MACRO E_SOURCE_EXTENSION_TASK_LIST ##### -->
+<para>
+
+</para>
+
+
+
diff --git a/docs/reference/libedataserver/Makefile.am b/docs/reference/libedataserver/Makefile.am
index a93fce2..7a48782 100644
--- a/docs/reference/libedataserver/Makefile.am
+++ b/docs/reference/libedataserver/Makefile.am
@@ -16,8 +16,10 @@ CFILE_GLOB = $(top_srcdir)/libedataserver/*.c
 
 IGNORE_HFILES = 					\
 	e-client-private.h				\
+	e-marshal.h					\
 	e-gdbus-marshallers.h				\
 	e-gdbus-templates.h				\
+	e-source-enumtypes.h				\
 	libedataserver-private.h
 
 GTKDOC_CFLAGS  =					\
diff --git a/docs/reference/libedataserver/libedataserver-docs.sgml b/docs/reference/libedataserver/libedataserver-docs.sgml
index f1ab381..8faec93 100644
--- a/docs/reference/libedataserver/libedataserver-docs.sgml
+++ b/docs/reference/libedataserver/libedataserver-docs.sgml
@@ -11,35 +11,37 @@
   <chapter>
     <title>Data Sources</title>
     <xi:include href="xml/e-source.xml"/>
-    <xi:include href="xml/e-source-password.xml"/>
     <xi:include href="xml/e-source-registry.xml"/>
+    <xi:include href="xml/e-source-authenticator.xml"/>
     <xi:include href="xml/e-source-extension.xml"/>
+    <xi:include href="xml/e-source-backend.xml"/>
+    <xi:include href="xml/e-source-selectable.xml"/>
+    <xi:include href="xml/e-source-address-book.xml"/>
     <xi:include href="xml/e-source-alarms.xml"/>
     <xi:include href="xml/e-source-authentication.xml"/>
     <xi:include href="xml/e-source-autocomplete.xml"/>
+    <xi:include href="xml/e-source-calendar.xml"/>
+    <xi:include href="xml/e-source-camel.xml"/>
+    <xi:include href="xml/e-source-collection.xml"/>
+    <xi:include href="xml/e-source-goa.xml"/>
+    <xi:include href="xml/e-source-mail-account.xml"/>
+    <xi:include href="xml/e-source-mail-composition.xml"/>
     <xi:include href="xml/e-source-mail-identity.xml"/>
+    <xi:include href="xml/e-source-mail-signature.xml"/>
+    <xi:include href="xml/e-source-mail-submission.xml"/>
+    <xi:include href="xml/e-source-mail-transport.xml"/>
+    <xi:include href="xml/e-source-mdn.xml"/>
+    <xi:include href="xml/e-source-memo-list.xml"/>
     <xi:include href="xml/e-source-offline.xml"/>
+    <xi:include href="xml/e-source-openpgp.xml"/>
     <xi:include href="xml/e-source-refresh.xml"/>
     <xi:include href="xml/e-source-security.xml"/>
-    <xi:include href="xml/e-source-selectable.xml"/>
+    <xi:include href="xml/e-source-smime.xml"/>
+    <xi:include href="xml/e-source-task-list.xml"/>
     <xi:include href="xml/e-source-webdav.xml"/>
   </chapter>
 
   <chapter>
-    <title>Generated Types</title>
-    <xi:include href="xml/e-dbus-object.xml"/>
-    <xi:include href="xml/e-dbus-object-proxy.xml"/>
-    <xi:include href="xml/e-dbus-object-skeleton.xml"/>
-    <xi:include href="xml/e-dbus-source.xml"/>
-    <xi:include href="xml/e-dbus-source-proxy.xml"/>
-    <xi:include href="xml/e-dbus-source-skeleton.xml"/>
-    <xi:include href="xml/e-dbus-source-manager.xml"/>
-    <xi:include href="xml/e-dbus-source-manager-proxy.xml"/>
-    <xi:include href="xml/e-dbus-source-manager-skeleton.xml"/>
-    <xi:include href="xml/e-dbus-object-manager-client.xml"/>
-  </chapter>
-
-  <chapter>
     <title>Miscellaneous Utilities</title>
     <xi:include href="xml/e-categories.xml"/>
     <xi:include href="xml/e-client.xml"/>
@@ -50,7 +52,6 @@
     <xi:include href="xml/e-operation-pool.xml"/>
     <xi:include href="xml/e-proxy.xml"/>
     <xi:include href="xml/e-sexp.xml"/>
-    <xi:include href="xml/e-source.xml"/>
     <xi:include href="xml/e-time-utils.xml"/>
     <xi:include href="xml/e-uid.xml"/>
     <xi:include href="xml/e-util.xml"/>
diff --git a/docs/reference/libedataserver/libedataserver-sections.txt b/docs/reference/libedataserver/libedataserver-sections.txt
index f14bc09..dd63213 100644
--- a/docs/reference/libedataserver/libedataserver-sections.txt
+++ b/docs/reference/libedataserver/libedataserver-sections.txt
@@ -237,37 +237,34 @@ e_proxy_get_type
 <TITLE>ESource</TITLE>
 ESource
 e_source_new
-e_source_new_with_absolute_uri
-e_source_new_from_xml_node
-e_source_new_from_standalone_xml
-e_source_copy
-e_source_update_from_xml_node
-e_source_uid_from_xml_node
-e_source_set_group
-e_source_set_name
-e_source_set_relative_uri
-e_source_set_absolute_uri
-e_source_set_color_spec
-e_source_set_readonly
-e_source_peek_group
-e_source_peek_uid
-e_source_peek_name
-e_source_peek_relative_uri
-e_source_peek_absolute_uri
-e_source_peek_color_spec
-e_source_get_readonly
-e_source_get_uri
-e_source_dump_to_xml_node
-e_source_to_standalone_xml
-e_source_get_property
-e_source_set_property
-e_source_foreach_property
-e_source_get_duped_property
-e_source_build_absolute_uri
+e_source_hash
 e_source_equal
-e_source_xmlstr_equal
+e_source_changed
 e_source_get_uid
+e_source_dup_uid
+e_source_get_parent
+e_source_dup_parent
+e_source_set_parent
+e_source_get_enabled
+e_source_set_enabled
+e_source_get_writable
+e_source_get_removable
+e_source_get_extension
+e_source_has_extension
+e_source_ref_dbus_object
+e_source_ref_main_context
 e_source_get_display_name
+e_source_dup_display_name
+e_source_set_display_name
+e_source_compare_by_display_name
+e_source_to_string
+e_source_parameter_to_key
+e_source_remove_sync
+e_source_remove
+e_source_remove_finish
+e_source_write_sync
+e_source_write
+e_source_write_finish
 <SUBSECTION Standard>
 E_SOURCE
 E_IS_SOURCE
@@ -282,6 +279,704 @@ e_source_get_type
 </SECTION>
 
 <SECTION>
+<FILE>e-source-address-book</FILE>
+<TITLE>ESourceAddressBook</TITLE>
+ESourceAddressBook
+E_SOURCE_EXTENSION_ADDRESS_BOOK
+<SUBSECTION Standard>
+E_SOURCE_ADDRESS_BOOK
+E_IS_SOURCE_ADDRESS_BOOK
+E_TYPE_SOURCE_ADDRESS_BOOK
+E_SOURCE_ADDRESS_BOOK_CLASS
+E_IS_SOURCE_ADDRESS_BOOK_CLASS
+E_SOURCE_ADDRESS_BOOK_GET_CLASS
+ESourceAddressBookClass
+<SUBSECTION Private>
+ESourceAddressBookPrivate
+e_source_address_book_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-alarms</FILE>
+<TITLE>ESourceAlarms</TITLE>
+ESourceAlarms
+E_SOURCE_EXTENSION_ALARMS
+e_source_alarms_get_include_me
+e_source_alarms_set_include_me
+e_source_alarms_get_last_notified
+e_source_alarms_dup_last_notified
+e_source_alarms_set_last_notified
+<SUBSECTION Standard>
+E_SOURCE_ALARMS
+E_IS_SOURCE_ALARMS
+E_TYPE_SOURCE_ALARMS
+E_SOURCE_ALARMS_CLASS
+E_IS_SOURCE_ALARMS_CLASS
+E_SOURCE_ALARMS_GET_CLASS
+ESourceAlarmsClass
+<SUBSECTION Private>
+ESourceAlarmsPrivate
+e_source_alarms_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-authentication</FILE>
+<TITLE>ESourceAuthentication</TITLE>
+ESourceAuthentication
+E_SOURCE_EXTENSION_AUTHENTICATION
+e_source_authentication_required
+e_source_authentication_get_host
+e_source_authentication_dup_host
+e_source_authentication_set_host
+e_source_authentication_get_method
+e_source_authentication_dup_method
+e_source_authentication_set_method
+e_source_authentication_get_port
+e_source_authentication_set_port
+e_source_authentication_get_user
+e_source_authentication_dup_user
+e_source_authentication_set_user
+<SUBSECTION Standard>
+E_SOURCE_AUTHENTICATION
+E_IS_SOURCE_AUTHENTICATION
+E_TYPE_SOURCE_AUTHENTICATION
+E_SOURCE_AUTHENTICATION_CLASS
+E_IS_SOURCE_AUTHENTICATION_CLASS
+E_SOURCE_AUTHENTICATION_GET_CLASS
+ESourceAuthenticationClass
+<SUBSECTION Private>
+ESourceAuthenticationPrivate
+e_source_authentication_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-authenticator</FILE>
+<TITLE>ESourceAuthenticator</TITLE>
+ESourceAuthenticator
+ESourceAuthenticatorInterface
+ESourceAuthenticationResult
+e_source_authenticator_get_prompt_strings
+e_source_authenticator_try_password_sync
+e_source_authenticator_try_password
+e_source_authenticator_try_password_finish
+<SUBSECTION Standard>
+E_SOURCE_AUTHENTICATOR
+E_IS_SOURCE_AUTHENTICATOR
+E_TYPE_SOURCE_AUTHENTICATOR
+E_SOURCE_AUTHENTICATOR_GET_INTERFACE
+<SUBSECTION Private>
+e_source_authenticator_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-autocomplete</FILE>
+<TITLE>ESourceAutocomplete</TITLE>
+ESourceAutocomplete
+E_SOURCE_EXTENSION_AUTOCOMPLETE
+e_source_autocomplete_get_include_me
+e_source_autocomplete_set_include_me
+<SUBSECTION Standard>
+E_SOURCE_AUTOCOMPLETE
+E_IS_SOURCE_AUTOCOMPLETE
+E_TYPE_SOURCE_AUTOCOMPLETE
+E_SOURCE_AUTOCOMPLETE_CLASS
+E_IS_SOURCE_AUTOCOMPLETE_CLASS
+E_SOURCE_AUTOCOMPLETE_GET_CLASS
+ESourceAutocompleteClass
+<SUBSECTION Private>
+ESourceAutocompletePrivate
+e_source_autocomplete_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-backend</FILE>
+<TITLE>ESourceBackend</TITLE>
+ESourceBackend
+e_source_backend_get_backend_name
+e_source_backend_dup_backend_name
+e_source_backend_set_backend_name
+<SUBSECTION Standard>
+E_SOURCE_BACKEND
+E_IS_SOURCE_BACKEND
+E_TYPE_SOURCE_BACKEND
+E_SOURCE_BACKEND_CLASS
+E_IS_SOURCE_BACKEND_CLASS
+E_SOURCE_BACKEND_GET_CLASS
+ESourceBackendClass
+<SUBSECTION Private>
+ESourceBackendPrivate
+e_source_backend_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-calendar</FILE>
+<TITLE>ESourceCalendar</TITLE>
+ESourceCalendar
+E_SOURCE_EXTENSION_CALENDAR
+<SUBSECTION Standard>
+E_SOURCE_CALENDAR
+E_IS_SOURCE_CALENDAR
+E_TYPE_SOURCE_CALENDAR
+E_SOURCE_CALENDAR_CLASS
+E_IS_SOURCE_CALENDAR_CLASS
+E_SOURCE_CALENDAR_GET_CLASS
+ESourceCalendarClass
+<SUBSECTION Private>
+ESourceCalendarPrivate
+e_source_calendar_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-camel</FILE>
+<TITLE>ESourceCamel</TITLE>
+ESourceCamel
+e_source_camel_register_types
+e_source_camel_generate_subtype
+e_source_camel_get_settings
+e_source_camel_get_type_name
+e_source_camel_get_extension_name
+e_source_camel_configure_service
+<SUBSECTION Standard>
+E_SOURCE_CAMEL
+E_IS_SOURCE_CAMEL
+E_TYPE_SOURCE_CAMEL
+E_SOURCE_CAMEL_CLASS
+E_IS_SOURCE_CAMEL_CLASS
+E_SOURCE_CAMEL_GET_CLASS
+ESourceCamelClass
+<SUBSECTION Private>
+ESourceCamelPrivate
+e_source_camel_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-collection</FILE>
+<TITLE>ESourceCollection</TITLE>
+ESourceCollection
+E_SOURCE_EXTENSION_COLLECTION
+e_source_collection_get_identity
+e_source_collection_dup_identity
+e_source_collection_set_identity
+e_source_collection_get_calendar_enabled
+e_source_collection_set_calendar_enabled
+e_source_collection_get_contacts_enabled
+e_source_collection_set_contacts_enabled
+e_source_collection_get_mail_enabled
+e_source_collection_set_mail_enabled
+<SUBSECTION Standard>
+E_SOURCE_COLLECTION
+E_IS_SOURCE_COLLECTION
+E_TYPE_SOURCE_COLLECTION
+E_SOURCE_COLLECTION_CLASS
+E_IS_SOURCE_COLLECTION_CLASS
+E_SOURCE_COLLECTION_GET_CLASS
+ESourceCollectionClass
+<SUBSECTION Private>
+ESourceCollectionPrivate
+e_source_collection_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-goa</FILE>
+<TITLE>ESourceGoa</TITLE>
+ESourceGoa
+E_SOURCE_EXTENSION_GOA
+e_source_goa_get_account_id
+e_source_goa_dup_account_id
+e_source_goa_set_account_id
+<SUBSECTION Standard>
+E_SOURCE_GOA
+E_IS_SOURCE_GOA
+E_TYPE_SOURCE_GOA
+E_SOURCE_GOA_CLASS
+E_IS_SOURCE_GOA_CLASS
+E_SOURCE_GOA_GET_CLASS
+ESourceGoaClass
+<SUBSECTION Private>
+ESourceGoaPrivate
+e_source_goa_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-extension</FILE>
+<TITLE>ESourceExtension</TITLE>
+ESourceExtension
+E_SOURCE_PARAM_SETTING
+e_source_extension_get_source
+<SUBSECTION Standard>
+E_SOURCE_EXTENSION
+E_IS_SOURCE_EXTENSION
+E_TYPE_SOURCE_EXTENSION
+E_SOURCE_EXTENSION_CLASS
+E_IS_SOURCE_EXTENSION_CLASS
+E_SOURCE_EXTENSION_GET_CLASS
+ESourceExtensionClass
+<SUBSECTION Private>
+ESourceExtensionPrivate
+e_source_extension_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-mail-account</FILE>
+<TITLE>ESourceMailAccount</TITLE>
+ESourceMailAccount
+E_SOURCE_EXTENSION_MAIL_ACCOUNT
+e_source_mail_account_get_identity_uid
+e_source_mail_account_dup_identity_uid
+e_source_mail_account_set_identity_uid
+<SUBSECTION Standard>
+E_SOURCE_MAIL_ACCOUNT
+E_IS_SOURCE_MAIL_ACCOUNT
+E_TYPE_SOURCE_MAIL_ACCOUNT
+E_SOURCE_MAIL_ACCOUNT_CLASS
+E_IS_SOURCE_MAIL_ACCOUNT_CLASS
+E_SOURCE_MAIL_ACCOUNT_GET_CLASS
+ESourceMailAccountClass
+<SUBSECTION Private>
+ESourceMailAccountPrivate
+e_source_mail_account_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-mail-composition</FILE>
+<TITLE>ESourceMailComposition</TITLE>
+ESourceMailComposition
+E_SOURCE_EXTENSION_MAIL_COMPOSITION
+e_source_mail_composition_get_bcc
+e_source_mail_composition_dup_bcc
+e_source_mail_composition_set_bcc
+e_source_mail_composition_get_cc
+e_source_mail_composition_dup_cc
+e_source_mail_composition_set_cc
+e_source_mail_composition_get_drafts_folder
+e_source_mail_composition_dup_drafts_folder
+e_source_mail_composition_set_drafts_folder
+e_source_mail_composition_get_sign_imip
+e_source_mail_composition_set_sign_imip
+e_source_mail_composition_get_templates_folder
+e_source_mail_composition_dup_templates_folder
+e_source_mail_composition_set_templates_folder
+<SUBSECTION Standard>
+E_SOURCE_MAIL_COMPOSITION
+E_IS_SOURCE_MAIL_COMPOSITION
+E_TYPE_SOURCE_MAIL_COMPOSITION
+E_SOURCE_MAIL_COMPOSITION_CLASS
+E_IS_SOURCE_MAIL_COMPOSITION_CLASS
+E_SOURCE_MAIL_COMPOSITION_GET_CLASS
+ESourceMailCompositionClass
+<SUBSECTION Private>
+ESourceMailCompositionPrivate
+e_source_mail_composition_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-mail-identity</FILE>
+<TITLE>ESourceMailIdentity</TITLE>
+ESourceMailIdentity
+E_SOURCE_EXTENSION_MAIL_IDENTITY
+e_source_mail_identity_get_address
+e_source_mail_identity_dup_address
+e_source_mail_identity_set_address
+e_source_mail_identity_get_name
+e_source_mail_identity_dup_name
+e_source_mail_identity_set_name
+e_source_mail_identity_get_organization
+e_source_mail_identity_dup_organization
+e_source_mail_identity_set_organization
+e_source_mail_identity_get_reply_to
+e_source_mail_identity_dup_reply_to
+e_source_mail_identity_set_reply_to
+e_source_mail_identity_get_signature_uid
+e_source_mail_identity_dup_signature_uid
+e_source_mail_identity_set_signature_uid
+<SUBSECTION Standard>
+E_SOURCE_MAIL_IDENTITY
+E_IS_SOURCE_MAIL_IDENTITY
+E_TYPE_SOURCE_MAIL_IDENTITY
+E_SOURCE_MAIL_IDENTITY_CLASS
+E_IS_SOURCE_MAIL_IDENTITY_CLASS
+E_SOURCE_MAIL_IDENTITY_GET_CLASS
+ESourceMailIdentityClass
+<SUBSECTION Private>
+ESourceMailIdentityPrivate
+e_source_mail_identity_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-mail-signature</FILE>
+<TITLE>ESourceMailSignature</TITLE>
+ESourceMailSignature
+E_SOURCE_EXTENSION_MAIL_SIGNATURE
+e_source_mail_signature_get_file
+e_source_mail_signature_get_mime_type
+e_source_mail_signature_dup_mime_type
+e_source_mail_signature_set_mime_type
+e_source_mail_signature_load_sync
+e_source_mail_signature_load
+e_source_mail_signature_load_finish
+e_source_mail_signature_replace_sync
+e_source_mail_signature_replace
+e_source_mail_signature_replace_finish
+e_source_mail_signature_symlink_sync
+e_source_mail_signature_symlink
+e_source_mail_signature_symlink_finish
+<SUBSECTION Standard>
+E_SOURCE_MAIL_SIGNATURE
+E_IS_SOURCE_MAIL_SIGNATURE
+E_TYPE_SOURCE_MAIL_SIGNATURE
+E_SOURCE_MAIL_SIGNATURE_CLASS
+E_IS_SOURCE_MAIL_SIGNATURE_CLASS
+E_SOURCE_MAIL_SIGNATURE_GET_CLASS
+ESourceMailSignatureClass
+<SUBSECTION Private>
+ESourceMailSignaturePrivate
+e_source_mail_signature_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-mail-submission</FILE>
+<TITLE>ESourceMailSubmission</TITLE>
+ESourceMailSubmission
+E_SOURCE_EXTENSION_MAIL_SUBMISSION
+e_source_mail_submission_get_sent_folder
+e_source_mail_submission_dup_sent_folder
+e_source_mail_submission_set_sent_folder
+e_source_mail_submission_get_transport_uid
+e_source_mail_submission_dup_transport_uid
+e_source_mail_submission_set_transport_uid
+<SUBSECTION Standard>
+E_SOURCE_MAIL_SUBMISSION
+E_IS_SOURCE_MAIL_SUBMISSION
+E_TYPE_SOURCE_MAIL_SUBMISSION
+E_SOURCE_MAIL_SUBMISSION_CLASS
+E_IS_SOURCE_MAIL_SUBMISSION_CLASS
+E_SOURCE_MAIL_SUBMISSION_GET_CLASS
+ESourceMailSubmissionClass
+<SUBSECTION Private>
+ESourceMailSubmissionPrivate
+e_source_mail_submission_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-mail-transport</FILE>
+<TITLE>ESourceMailTransport</TITLE>
+ESourceMailTransport
+E_SOURCE_EXTENSION_MAIL_TRANSPORT
+<SUBSECTION Standard>
+E_SOURCE_MAIL_TRANSPORT
+E_IS_SOURCE_MAIL_TRANSPORT
+E_TYPE_SOURCE_MAIL_TRANSPORT
+E_SOURCE_MAIL_TRANSPORT_CLASS
+E_IS_SOURCE_MAIL_TRANSPORT_CLASS
+E_SOURCE_MAIL_TRANSPORT_GET_CLASS
+ESourceMailSubmissionClass
+<SUBSECTION Private>
+ESourceMailTransportPrivate
+e_source_mail_transport_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-mdn</FILE>
+<TITLE>ESourceMDN</TITLE>
+ESourceMDN
+E_SOURCE_EXTENSION_MDN
+EMdnResponsePolicy
+e_source_mdn_get_response_policy
+e_source_mdn_set_response_policy
+<SUBSECTION Standard>
+E_SOURCE_MDN
+E_IS_SOURCE_MDN
+E_TYPE_SOURCE_MDN
+E_SOURCE_MDN_CLASS
+E_IS_SOURCE_MDN_CLASS
+E_SOURCE_MDN_GET_CLASS
+ESourceMDNClass
+<SUBSECTION Private>
+ESourceMDNPrivate
+e_source_mdn_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-memo-list</FILE>
+<TITLE>ESourceMemoList</TITLE>
+ESourceMemoList
+E_SOURCE_EXTENSION_MEMO_LIST
+<SUBSECTION Standard>
+E_SOURCE_MEMO_LIST
+E_IS_SOURCE_MEMO_LIST
+E_TYPE_SOURCE_MEMO_LIST
+E_SOURCE_MEMO_LIST_CLASS
+E_IS_SOURCE_MEMO_LIST_CLASS
+E_SOURCE_MEMO_LIST_GET_CLASS
+ESourceMemoListClass
+<SUBSECTION Private>
+ESourceMemoListPrivate
+e_source_memo_list_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-offline</FILE>
+<TITLE>ESourceOffline</TITLE>
+ESourceOffline
+E_SOURCE_EXTENSION_OFFLINE
+e_source_offline_get_stay_synchronized
+e_source_offline_set_stay_synchronized
+<SUBSECTION Standard>
+E_SOURCE_OFFLINE
+E_IS_SOURCE_OFFLINE
+E_TYPE_SOURCE_OFFLINE
+E_SOURCE_OFFLINE_CLASS
+E_IS_SOURCE_OFFLINE_CLASS
+E_SOURCE_OFFLINE_GET_CLASS
+ESourceOfflineClass
+<SUBSECTION Private>
+ESourceOfflinePrivate
+e_source_offline_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-openpgp</FILE>
+<TITLE>ESourceOpenPGP</TITLE>
+ESourceOpenPGP
+E_SOURCE_EXTENSION_OPENPGP
+e_source_openpgp_get_always_trust
+e_source_openpgp_set_always_trust
+e_source_openpgp_get_encrypt_to_self
+e_source_openpgp_set_encrypt_to_self
+e_source_openpgp_get_key_id
+e_source_openpgp_dup_key_id
+e_source_openpgp_set_key_id
+e_source_openpgp_get_signing_algorithm
+e_source_openpgp_dup_signing_algorithm
+e_source_openpgp_set_signing_algorithm
+e_source_openpgp_get_sign_by_default
+e_source_openpgp_set_sign_by_default
+<SUBSECTION Standard>
+E_SOURCE_OPENPGP
+E_IS_SOURCE_OPENPGP
+E_TYPE_SOURCE_OPENPGP
+E_SOURCE_OPENPGP_CLASS
+E_IS_SOURCE_OPENPGP_CLASS
+E_SOURCE_OPENPGP_GET_CLASS
+ESourceOpenPGPClass
+<SUBSECTION Private>
+ESourceOpenPGPPrivate
+e_source_openpgp_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-refresh</FILE>
+<TITLE>ESourceRefresh</TITLE>
+ESourceRefresh
+E_SOURCE_EXTENSION_REFRESH
+e_source_refresh_get_enabled
+e_source_refresh_set_enabled
+e_source_refresh_get_interval_minutes
+e_source_refresh_set_interval_minutes
+ESourceRefreshFunc
+e_source_refresh_add_timeout
+e_source_refresh_force_timeout
+e_source_refresh_remove_timeout
+e_source_refresh_remove_timeouts_by_data
+<SUBSECTION Standard>
+E_SOURCE_REFRESH
+E_IS_SOURCE_REFRESH
+E_TYPE_SOURCE_REFRESH
+E_SOURCE_REFRESH_CLASS
+E_IS_SOURCE_REFRESH_CLASS
+E_SOURCE_REFRESH_GET_CLASS
+ESourceRefreshClass
+<SUBSECTION Private>
+ESourceRefreshPrivate
+e_source_refresh_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-registry</FILE>
+<TITLE>ESourceRegistry</TITLE>
+ESourceRegistry
+e_source_registry_new_sync
+e_source_registry_new
+e_source_registry_new_finish
+e_source_registry_authenticate_sync
+e_source_registry_authenticate
+e_source_registry_authenticate_finish
+e_source_registry_commit_source_sync
+e_source_registry_commit_source
+e_source_registry_commit_source_finish
+e_source_registry_create_source_sync
+e_source_registry_create_source
+e_source_registry_create_source_finish
+e_source_registry_ref_source
+e_source_registry_list_sources
+e_source_registry_find_extension
+e_source_registry_build_display_tree
+e_source_registry_free_display_tree
+e_source_registry_debug_dump
+e_source_registry_ref_builtin_address_book
+e_source_registry_ref_default_address_book
+e_source_registry_set_default_address_book
+e_source_registry_ref_builtin_calendar
+e_source_registry_ref_default_calendar
+e_source_registry_set_default_calendar
+e_source_registry_ref_builtin_mail_account
+e_source_registry_ref_default_mail_account
+e_source_registry_set_default_mail_account
+e_source_registry_ref_default_mail_identity
+e_source_registry_set_default_mail_identity
+e_source_registry_ref_builtin_memo_list
+e_source_registry_ref_default_memo_list
+e_source_registry_set_default_memo_list
+e_source_registry_ref_builtin_task_list
+e_source_registry_ref_default_task_list
+e_source_registry_set_default_task_list
+e_source_registry_ref_default_for_extension_name
+e_source_registry_set_default_for_extension_name
+<SUBSECTION Standard>
+E_SOURCE_REGISTRY
+E_IS_SOURCE_REGISTRY
+E_TYPE_SOURCE_REGISTRY
+E_SOURCE_REGISTRY_CLASS
+E_IS_SOURCE_REGISTRY_CLASS
+E_SOURCE_REGISTRY_GET_CLASS
+ESourceRegistryClass
+<SUBSECTION Private>
+ESourceRegistryPrivate
+e_source_registry_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-security</FILE>
+<TITLE>ESourceSecurity</TITLE>
+ESourceSecurity
+E_SOURCE_EXTENSION_SECURITY
+e_source_security_get_method
+e_source_security_dup_method
+e_source_security_set_method
+e_source_security_get_secure
+e_source_security_set_secure
+<SUBSECTION Standard>
+E_SOURCE_SECURITY
+E_IS_SOURCE_SECURITY
+E_TYPE_SOURCE_SECURITY
+E_SOURCE_SECURITY_CLASS
+E_IS_SOURCE_SECURITY_CLASS
+E_SOURCE_SECURITY_GET_CLASS
+ESourceSecurityClass
+<SUBSECTION Private>
+ESourceSecurityPrivate
+e_source_security_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-selectable</FILE>
+<TITLE>ESourceSelectable</TITLE>
+ESourceSelectable
+e_source_selectable_get_color
+e_source_selectable_dup_color
+e_source_selectable_set_color
+e_source_selectable_get_selected
+e_source_selectable_set_selected
+<SUBSECTION Standard>
+E_SOURCE_SELECTABLE
+E_IS_SOURCE_SELECTABLE
+E_TYPE_SOURCE_SELECTABLE
+E_SOURCE_SELECTABLE_CLASS
+E_IS_SOURCE_SELECTABLE_CLASS
+E_SOURCE_SELECTABLE_GET_CLASS
+ESourceSelectableClass
+<SUBSECTION Private>
+ESourceSelectablePrivate
+e_source_selectable_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-smime</FILE>
+<TITLE>ESourceSMIME</TITLE>
+ESourceSMIME
+E_SOURCE_EXTENSION_SMIME
+e_source_smime_get_encryption_certificate
+e_source_smime_dup_encryption_certificate
+e_source_smime_set_encryption_certificate
+e_source_smime_get_encrypt_by_default
+e_source_smime_set_encrypt_by_default
+e_source_smime_get_encrypt_to_self
+e_source_smime_set_encrypt_to_self
+e_source_smime_get_signing_algorithm
+e_source_smime_dup_signing_algorithm
+e_source_smime_set_signing_algorithm
+e_source_smime_get_signing_certificate
+e_source_smime_dup_signing_certificate
+e_source_smime_set_signing_certificate
+e_source_smime_get_sign_by_default
+e_source_smime_set_sign_by_default
+<SUBSECTION Standard>
+E_SOURCE_SMIME
+E_IS_SOURCE_SMIME
+E_TYPE_SOURCE_SMIME
+E_SOURCE_SMIME_CLASS
+E_IS_SOURCE_SMIME_CLASS
+E_SOURCE_SMIME_GET_CLASS
+ESourceSMIMEClass
+<SUBSECTION Private>
+ESourceSMIMEPrivate
+e_source_smime_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-task-list</FILE>
+<TITLE>ESourceTaskList</TITLE>
+ESourceTaskList
+E_SOURCE_EXTENSION_TASK_LIST
+<SUBSECTION Standard>
+E_SOURCE_TASK_LIST
+E_IS_SOURCE_TASK_LIST
+E_TYPE_SOURCE_TASK_LIST
+E_SOURCE_TASK_LIST_CLASS
+E_IS_SOURCE_TASK_LIST_CLASS
+E_SOURCE_TASK_LIST_GET_CLASS
+ESourceTaskListClass
+<SUBSECTION Private>
+ESourceTaskListPrivate
+e_source_task_list_get_type
+</SECTION>
+
+<SECTION>
+<FILE>e-source-webdav</FILE>
+<TITLE>ESourceWebdav</TITLE>
+ESourceWebdav
+E_SOURCE_EXTENSION_WEBDAV_BACKEND
+e_source_webdav_get_calendar_auto_schedule
+e_source_webdav_set_calendar_auto_schedule
+e_source_webdav_get_display_name
+e_source_webdav_dup_display_name
+e_source_webdav_set_display_name
+e_source_webdav_get_email_address
+e_source_webdav_dup_email_address
+e_source_webdav_set_email_address
+e_source_webdav_get_ignore_invalid_cert
+e_source_webdav_set_ignore_invalid_cert
+e_source_webdav_get_resource_path
+e_source_webdav_dup_resource_path
+e_source_webdav_set_resource_path
+e_source_webdav_dup_soup_uri
+e_source_webdav_set_soup_uri
+e_source_webdav_get_avoid_ifmatch
+e_source_webdav_set_avoid_ifmatch
+<SUBSECTION Standard>
+E_SOURCE_WEBDAV
+E_IS_SOURCE_WEBDAV
+E_TYPE_SOURCE_WEBDAV
+E_SOURCE_WEBDAV_CLASS
+E_IS_SOURCE_WEBDAV_CLASS
+E_SOURCE_WEBDAV_GET_CLASS
+ESourceWebdavClass
+<SUBSECTION Private>
+ESourceWebdavPrivate
+e_source_webdav_get_type
+</SECTION>
+
+<SECTION>
 <FILE>e-categories</FILE>
 e_categories_get_list
 e_categories_add
diff --git a/docs/reference/libedataserver/libedataserver.types b/docs/reference/libedataserver/libedataserver.types
index f7ced4b..586f7c9 100644
--- a/docs/reference/libedataserver/libedataserver.types
+++ b/docs/reference/libedataserver/libedataserver.types
@@ -4,6 +4,30 @@
 #include <libedataserver/e-list-iterator.h>
 #include <libedataserver/e-proxy.h>
 #include <libedataserver/e-source.h>
+#include <libedataserver/e-source-address-book.h>
+#include <libedataserver/e-source-authentication.h>
+#include <libedataserver/e-source-authenticator.h>
+#include <libedataserver/e-source-backend.h>
+#include <libedataserver/e-source-calendar.h>
+#include <libedataserver/e-source-camel.h>
+#include <libedataserver/e-source-collection.h>
+#include <libedataserver/e-source-extension.h>
+#include <libedataserver/e-source-goa.h>
+#include <libedataserver/e-source-mail-account.h>
+#include <libedataserver/e-source-mail-composition.h>
+#include <libedataserver/e-source-mail-identity.h>
+#include <libedataserver/e-source-mail-signature.h>
+#include <libedataserver/e-source-mail-submission.h>
+#include <libedataserver/e-source-mail-transport.h>
+#include <libedataserver/e-source-mdn.h>
+#include <libedataserver/e-source-offline.h>
+#include <libedataserver/e-source-openpgp.h>
+#include <libedataserver/e-source-refresh.h>
+#include <libedataserver/e-source-registry.h>
+#include <libedataserver/e-source-security.h>
+#include <libedataserver/e-source-selectable.h>
+#include <libedataserver/e-source-smime.h>
+#include <libedataserver/e-source-webdav.h>
 
 e_client_get_type
 e_iterator_get_type
@@ -11,4 +35,30 @@ e_list_get_type
 e_list_iterator_get_type
 e_proxy_get_type
 e_source_get_type
+e_source_address_book_get_type
+e_source_authentication_get_type
+e_source_authenticator_get_type
+e_source_backend_get_type
+e_source_calendar_get_type
+e_source_camel_get_type
+e_source_collection_get_type
+e_source_extension_get_type
+e_source_goa_get_type
+e_source_mail_account_get_type
+e_source_mail_composition_get_type
+e_source_mail_identity_get_type
+e_source_mail_signature_get_type
+e_source_mail_submission_get_type
+e_source_mail_transport_get_type
+e_source_mdn_get_type
+e_source_memo_list_get_type
+e_source_offline_get_type
+e_source_openpgp_get_type
+e_source_refresh_get_type
+e_source_registry_get_type
+e_source_security_get_type
+e_source_selectable_get_type
+e_source_smime_get_type
+e_source_task_list_get_type
+e_source_webdav_get_type
 
diff --git a/docs/reference/libedataserverui/libedataserverui-sections.txt b/docs/reference/libedataserverui/libedataserverui-sections.txt
index f0b4daa..652af90 100644
--- a/docs/reference/libedataserverui/libedataserverui-sections.txt
+++ b/docs/reference/libedataserverui/libedataserverui-sections.txt
@@ -391,7 +391,8 @@ e_tree_model_generator_get_type
 <TITLE>ESourceSelector</TITLE>
 ESourceSelector
 e_source_selector_new
-e_source_selector_get_source_list
+e_source_selector_get_registry
+e_source_selector_get_extension_name
 e_source_selector_select_source
 e_source_selector_unselect_source
 e_source_selector_select_exclusive
@@ -404,7 +405,6 @@ e_source_selector_set_select_new
 e_source_selector_edit_primary_selection
 e_source_selector_ref_primary_selection
 e_source_selector_set_primary_selection
-e_source_selector_get_primary_source_group
 e_source_selector_peek_primary_selection
 e_source_selector_ref_source_by_path
 <SUBSECTION Standard>
diff --git a/libebackend/e-source-registry-server.c b/libebackend/e-source-registry-server.c
index 43cf71e..7dd28bf 100644
--- a/libebackend/e-source-registry-server.c
+++ b/libebackend/e-source-registry-server.c
@@ -1065,11 +1065,11 @@ e_source_registry_server_class_init (ESourceRegistryServerClass *class)
 		G_TYPE_NONE, 0);
 
 	/**
-	 * ESourceRegistryServer::source-added
+	 * ESourceRegistryServer::source-added:
 	 * @server: the #ESourceRegistryServer which emitted the signal
 	 * @source: the newly-added #EServerSideSource
 	 *
-	 * Emitted when an #EDBusSourceObject is added to @server.
+	 * Emitted when an #EServerSideSource is added to @server.
 	 **/
 	signals[SOURCE_ADDED] = g_signal_new (
 		"source-added",
@@ -1082,11 +1082,11 @@ e_source_registry_server_class_init (ESourceRegistryServerClass *class)
 		E_TYPE_SERVER_SIDE_SOURCE);
 
 	/**
-	 * ESourceRegistryServer::source-removed
+	 * ESourceRegistryServer::source-removed:
 	 * @server: the #ESourceRegistryServer when emitted the signal
 	 * @source: the #EServerSideSource that got removed
 	 *
-	 * Emitted when an #EDBusSourceObject is removed from @server.
+	 * Emitted when an #EServerSideSource is removed from @server.
 	 **/
 	signals[SOURCE_REMOVED] = g_signal_new (
 		"source-removed",
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index c1eb91d..3a7b83b 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -1,7 +1,17 @@
+include $(top_srcdir)/glib-gen.mak
+glib_enum_headers=e-source-enums.h
+glib_enum_output=e-source-enumtypes
+glib_enum_define=E
+glib_enum_prefix=e
+
+ENUM_GENERATED = e-source-enumtypes.h e-source-enumtypes.c
+
 # The marshallers
 MARSHAL_GENERATED = \
 	e-gdbus-marshallers.c \
-	e-gdbus-marshallers.h
+	e-gdbus-marshallers.h \
+	e-marshal.c \
+	e-marshal.h
 @EVO_MARSHAL_RULE@
 
 BUILT_SOURCES = \
@@ -19,9 +29,11 @@ libedataserver_1_2_la_CPPFLAGS = \
 	-DE_DATA_SERVER_LOCALEDIR=\""$(localedir)"\"			\
 	-DE_DATA_SERVER_EXTENSIONDIR=\"$(extensiondir)\"		\
 	-DE_DATA_SERVER_IMAGESDIR=\"$(imagesdir)\"			\
+	-DE_DATA_SERVER_PRIVDATADIR=\"$(privdatadir)\"			\
 	-DE_DATA_SERVER_UI_UIDIR=\""$(uidir)"\"				\
 	-DDEFAULT_EDS_DBUS_TIMEOUT=$(DEFAULT_EDS_DBUS_TIMEOUT)		\
 	$(E_DATA_SERVER_CFLAGS)						\
+	$(GCR_BASE_CFLAGS)						\
 	$(GIO_UNIX_CFLAGS)						\
 	$(SOUP_CFLAGS)							\
 	$(CODE_COVERAGE_CFLAGS)						\
@@ -43,6 +55,32 @@ libedataserver_1_2_la_SOURCES =		\
 	e-proxy.c			\
 	e-sexp.c			\
 	e-source.c			\
+	e-source-extension.c		\
+	e-source-address-book.c		\
+	e-source-alarms.c		\
+	e-source-authentication.c	\
+	e-source-authenticator.c	\
+	e-source-autocomplete.c		\
+	e-source-backend.c		\
+	e-source-calendar.c		\
+	e-source-camel.c		\
+	e-source-collection.c		\
+	e-source-goa.c			\
+	e-source-mail-account.c		\
+	e-source-mail-composition.c	\
+	e-source-mail-identity.c	\
+	e-source-mail-signature.c	\
+	e-source-mail-submission.c	\
+	e-source-mail-transport.c	\
+	e-source-mdn.c			\
+	e-source-offline.c		\
+	e-source-openpgp.c		\
+	e-source-refresh.c		\
+	e-source-registry.c		\
+	e-source-security.c		\
+	e-source-selectable.c		\
+	e-source-smime.c		\
+	e-source-webdav.c		\
 	e-debug-log.c			\
 	e-time-utils.c			\
 	e-uid.c				\
@@ -57,6 +95,7 @@ libedataserver_1_2_la_LIBADD =				\
 	$(top_builddir)/camel/libcamel-1.2.la		\
 	$(top_builddir)/private/libedbus-private.la	\
 	$(E_DATA_SERVER_LIBS)				\
+	$(GCR_BASE_LIBS)				\
 	$(GIO_UNIX_LIBS)				\
 	$(ICONV_LIBS)					\
 	$(SOCKET_LIBS)					\
@@ -85,6 +124,34 @@ libedataserverinclude_HEADERS =		\
 	e-proxy.h			\
 	e-sexp.h			\
 	e-source.h			\
+	e-source-address-book.h		\
+	e-source-alarms.h		\
+	e-source-authentication.h	\
+	e-source-authenticator.h	\
+	e-source-autocomplete.h		\
+	e-source-backend.h		\
+	e-source-calendar.h		\
+	e-source-camel.h		\
+	e-source-collection.h		\
+	e-source-enums.h		\
+	e-source-enumtypes.h		\
+	e-source-extension.h		\
+	e-source-goa.h			\
+	e-source-mail-account.h		\
+	e-source-mail-composition.h	\
+	e-source-mail-identity.h	\
+	e-source-mail-signature.h	\
+	e-source-mail-submission.h	\
+	e-source-mail-transport.h	\
+	e-source-mdn.h			\
+	e-source-offline.h		\
+	e-source-openpgp.h		\
+	e-source-refresh.h		\
+	e-source-registry.h		\
+	e-source-security.h		\
+	e-source-selectable.h		\
+	e-source-smime.h		\
+	e-source-webdav.h		\
 	e-debug-log.h			\
 	e-time-utils.h			\
 	e-uid.h				\
diff --git a/libedataserver/e-marshal.list b/libedataserver/e-marshal.list
new file mode 100644
index 0000000..4abc0be
--- /dev/null
+++ b/libedataserver/e-marshal.list
@@ -0,0 +1 @@
+NONE:OBJECT,BOXED
diff --git a/libedataserver/e-source-address-book.c b/libedataserver/e-source-address-book.c
new file mode 100644
index 0000000..2687f1d
--- /dev/null
+++ b/libedataserver/e-source-address-book.c
@@ -0,0 +1,59 @@
+/*
+ * e-source-address-book.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-address-book
+ * @include: libedataserver/e-source-address-book.h
+ * @short_description: #ESource extension for an address book
+ *
+ * The #ESourceAddressBook extension identifies the #ESource as an
+ * address book.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-address-book.h>
+ *
+ *   ESourceAddressBook *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
+ * ]|
+ **/
+
+#include "e-source-address-book.h"
+
+#include <libedataserver/e-data-server-util.h>
+
+G_DEFINE_TYPE (
+	ESourceAddressBook,
+	e_source_address_book,
+	E_TYPE_SOURCE_BACKEND)
+
+static void
+e_source_address_book_class_init (ESourceAddressBookClass *class)
+{
+	ESourceExtensionClass *extension_class;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+}
+
+static void
+e_source_address_book_init (ESourceAddressBook *extension)
+{
+}
diff --git a/libedataserver/e-source-address-book.h b/libedataserver/e-source-address-book.h
new file mode 100644
index 0000000..bd9dc25
--- /dev/null
+++ b/libedataserver/e-source-address-book.h
@@ -0,0 +1,80 @@
+/*
+ * e-source-address-book.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_SOURCE_ADDRESS_BOOK_H
+#define E_SOURCE_ADDRESS_BOOK_H
+
+#include <libedataserver/e-source-backend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_ADDRESS_BOOK \
+	(e_source_address_book_get_type ())
+#define E_SOURCE_ADDRESS_BOOK(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_ADDRESS_BOOK, ESourceAddressBook))
+#define E_SOURCE_ADDRESS_BOOK_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_ADDRESS_BOOK, ESourceAddressBookClass))
+#define E_IS_SOURCE_ADDRESS_BOOK(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_ADDRESS_BOOK))
+#define E_IS_SOURCE_ADDRESS_BOOK_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_ADDRESS_BOOK))
+#define E_SOURCE_ADDRESS_BOOK_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_ADDRESS_BOOK, ESourceAddressBookClass))
+
+/**
+ * E_SOURCE_EXTENSION_ADDRESS_BOOK:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceAddressBook.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_ADDRESS_BOOK "Address Book"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceAddressBook ESourceAddressBook;
+typedef struct _ESourceAddressBookClass ESourceAddressBookClass;
+typedef struct _ESourceAddressBookPrivate ESourceAddressBookPrivate;
+
+/**
+ * ESourceAddressBook:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceAddressBook {
+	ESourceBackend parent;
+	ESourceAddressBookPrivate *priv;
+};
+
+struct _ESourceAddressBookClass {
+	ESourceBackendClass parent_class;
+};
+
+GType		e_source_address_book_get_type	(void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* E_SOURCE_ADDRESS_BOOK_H */
diff --git a/libedataserver/e-source-alarms.c b/libedataserver/e-source-alarms.c
new file mode 100644
index 0000000..e5213b8
--- /dev/null
+++ b/libedataserver/e-source-alarms.c
@@ -0,0 +1,312 @@
+/*
+ * e-source-alarms.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-alarms
+ * @include: libedataserver/e-source-alarms.h
+ * @short_description: #ESource extension for alarm state
+ *
+ * The #ESourceAlarms extension tracks alarm state for a calendar.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-alarms.h>
+ *
+ *   ESourceAlarms *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_ALARMS);
+ * ]|
+ **/
+
+#include "e-source-alarms.h"
+
+#define E_SOURCE_ALARMS_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_ALARMS, ESourceAlarmsPrivate))
+
+struct _ESourceAlarmsPrivate {
+	GMutex *property_lock;
+	gboolean include_me;
+	gchar *last_notified;
+};
+
+enum {
+	PROP_0,
+	PROP_INCLUDE_ME,
+	PROP_LAST_NOTIFIED
+};
+
+G_DEFINE_TYPE (
+	ESourceAlarms,
+	e_source_alarms,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_alarms_set_property (GObject *object,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_INCLUDE_ME:
+			e_source_alarms_set_include_me (
+				E_SOURCE_ALARMS (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_LAST_NOTIFIED:
+			e_source_alarms_set_last_notified (
+				E_SOURCE_ALARMS (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_alarms_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_INCLUDE_ME:
+			g_value_set_boolean (
+				value,
+				e_source_alarms_get_include_me (
+				E_SOURCE_ALARMS (object)));
+			return;
+
+		case PROP_LAST_NOTIFIED:
+			g_value_take_string (
+				value,
+				e_source_alarms_dup_last_notified (
+				E_SOURCE_ALARMS (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_alarms_finalize (GObject *object)
+{
+	ESourceAlarmsPrivate *priv;
+
+	priv = E_SOURCE_ALARMS_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->last_notified);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_alarms_parent_class)->finalize (object);
+}
+
+static void
+e_source_alarms_class_init (ESourceAlarmsClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceAlarmsPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_alarms_set_property;
+	object_class->get_property = source_alarms_get_property;
+	object_class->finalize = source_alarms_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_ALARMS;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_INCLUDE_ME,
+		g_param_spec_boolean (
+			"include-me",
+			"IncludeMe",
+			"Include this source in alarm notifications",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_LAST_NOTIFIED,
+		g_param_spec_string (
+			"last-notified",
+			"LastNotified",
+			"Last alarm notification (in ISO 8601 format)",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_alarms_init (ESourceAlarms *extension)
+{
+	extension->priv = E_SOURCE_ALARMS_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_alarms_get_include_me:
+ * @extension: an #ESourceAlarms
+ *
+ * Returns whether the user should be alerted about upcoming appointments
+ * in the calendar described by the #ESource to which @extension belongs.
+ *
+ * Alarm daemons such as evolution-alarm-notify can use this property to
+ * decide which calendars to query for upcoming appointments.
+ *
+ * Returns: whether to show alarms for upcoming appointments
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_alarms_get_include_me (ESourceAlarms *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_ALARMS (extension), FALSE);
+
+	return extension->priv->include_me;
+}
+
+/**
+ * e_source_alarms_set_include_me:
+ * @extension: an #ESourceAlarms
+ * @include_me: whether to show alarms for upcoming appointments
+ *
+ * Sets whether the user should be alerted about upcoming appointments in
+ * the calendar described by the #ESource to which @extension belongs.
+ *
+ * Alarm daemons such as evolution-alarm-notify can use this property to
+ * decide which calendars to query for upcoming appointments.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_alarms_set_include_me (ESourceAlarms *extension,
+                                gboolean include_me)
+{
+	g_return_if_fail (E_IS_SOURCE_ALARMS (extension));
+
+	extension->priv->include_me = include_me;
+
+	g_object_notify (G_OBJECT (extension), "include-me");
+}
+
+/**
+ * e_source_alarms_get_last_notified:
+ * @extension: an #ESourceAlarms
+ *
+ * Returns an ISO 8601 formatted timestamp of when the user was last
+ * alerted about an upcoming appointment in the calendar described by
+ * the #ESource to which @extension belongs.  If no valid timestamp
+ * has been set, the function will return %NULL.
+ *
+ * Returns: an ISO 8601 timestamp, or %NULL
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_alarms_get_last_notified (ESourceAlarms *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_ALARMS (extension), NULL);
+
+	return extension->priv->last_notified;
+}
+
+/**
+ * e_source_alarms_dup_last_notified:
+ * @extension: an #ESourceAlarms
+ *
+ * Thread-safe variation of e_source_alarms_get_last_notified().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceAlarms:last-notified
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_alarms_dup_last_notified (ESourceAlarms *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_ALARMS (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_alarms_get_last_notified (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_alarms_set_last_notified:
+ * @extension: an #ESourceAlarms
+ * @last_notified: an ISO 8601 timestamp, or %NULL
+ *
+ * Sets an ISO 8601 formatted timestamp of when the user was last
+ * alerted about an upcoming appointment in the calendar described
+ * by the #ESource to which @extension belongs.
+ *
+ * If @last_notified is non-%NULL, the function will validate the
+ * timestamp before setting the #ESourceAlarms:last-notified property.
+ * Invalid timestamps are discarded with a runtime warning.
+ *
+ * Generally, this function should only be called by an alarm daemon
+ * such as evolution-alarm-notify.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_alarms_set_last_notified (ESourceAlarms *extension,
+                                   const gchar *last_notified)
+{
+	g_return_if_fail (E_IS_SOURCE_ALARMS (extension));
+
+	if (last_notified != NULL) {
+		GTimeVal time_val;
+
+		if (!g_time_val_from_iso8601 (last_notified, &time_val)) {
+			g_warning ("Invalid timestamp: %s", last_notified);
+			return;
+		}
+	}
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_free (extension->priv->last_notified);
+	extension->priv->last_notified = g_strdup (last_notified);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "last-notified");
+}
diff --git a/libedataserver/e-source-alarms.h b/libedataserver/e-source-alarms.h
new file mode 100644
index 0000000..cacc7d5
--- /dev/null
+++ b/libedataserver/e-source-alarms.h
@@ -0,0 +1,90 @@
+/*
+ * e-source-alarms.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_SOURCE_ALARMS_H
+#define E_SOURCE_ALARMS_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_ALARMS \
+	(e_source_alarms_get_type ())
+#define E_SOURCE_ALARMS(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_ALARMS, ESourceAlarms))
+#define E_SOURCE_ALARMS_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_ALARMS, ESourceAlarmsClass))
+#define E_IS_SOURCE_ALARMS(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_ALARMS))
+#define E_IS_SOURCE_ALARMS_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_ALARMS))
+#define E_SOURCE_ALARMS_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_ALARMS, ESourceAlarmsClass))
+
+/**
+ * E_SOURCE_EXTENSION_ALARMS:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceAlarms.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_ALARMS "Alarms"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceAlarms ESourceAlarms;
+typedef struct _ESourceAlarmsClass ESourceAlarmsClass;
+typedef struct _ESourceAlarmsPrivate ESourceAlarmsPrivate;
+
+/**
+ * ESourceAlarms:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceAlarms {
+	ESourceExtension parent;
+	ESourceAlarmsPrivate *priv;
+};
+
+struct _ESourceAlarmsClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_alarms_get_type	(void) G_GNUC_CONST;
+gboolean	e_source_alarms_get_include_me	(ESourceAlarms *extension);
+void		e_source_alarms_set_include_me	(ESourceAlarms *extension,
+						 gboolean include_me);
+const gchar *	e_source_alarms_get_last_notified
+						(ESourceAlarms *extension);
+gchar *		e_source_alarms_dup_last_notified
+						(ESourceAlarms *extension);
+void		e_source_alarms_set_last_notified
+						(ESourceAlarms *extension,
+						 const gchar *last_notified);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_ALARMS_H */
diff --git a/libedataserver/e-source-authentication.c b/libedataserver/e-source-authentication.c
new file mode 100644
index 0000000..58a7dc2
--- /dev/null
+++ b/libedataserver/e-source-authentication.c
@@ -0,0 +1,575 @@
+/*
+ * e-source-authentication.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-authentication
+ * @include: libedataserver/e-source-authentication.h
+ * @short_description: #ESource extension for authentication settings
+ *
+ * The #ESourceAuthentication extension tracks authentication settings
+ * for a user account on a remote server.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-authentication.h>
+ *
+ *   ESourceAuthentication *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ * ]|
+ **/
+
+#include "e-source-authentication.h"
+
+#define E_SOURCE_AUTHENTICATION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthenticationPrivate))
+
+struct _ESourceAuthenticationPrivate {
+	GMutex *property_lock;
+	gchar *host;
+	gchar *method;
+	guint16 port;
+	gchar *user;
+};
+
+enum {
+	PROP_0,
+	PROP_HOST,
+	PROP_METHOD,
+	PROP_PORT,
+	PROP_USER
+};
+
+G_DEFINE_TYPE (
+	ESourceAuthentication,
+	e_source_authentication,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_authentication_set_property (GObject *object,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_HOST:
+			e_source_authentication_set_host (
+				E_SOURCE_AUTHENTICATION (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_METHOD:
+			e_source_authentication_set_method (
+				E_SOURCE_AUTHENTICATION (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_PORT:
+			e_source_authentication_set_port (
+				E_SOURCE_AUTHENTICATION (object),
+				g_value_get_uint (value));
+			return;
+
+		case PROP_USER:
+			e_source_authentication_set_user (
+				E_SOURCE_AUTHENTICATION (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_authentication_get_property (GObject *object,
+                                    guint property_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_HOST:
+			g_value_take_string (
+				value,
+				e_source_authentication_dup_host (
+				E_SOURCE_AUTHENTICATION (object)));
+			return;
+
+		case PROP_METHOD:
+			g_value_take_string (
+				value,
+				e_source_authentication_dup_method (
+				E_SOURCE_AUTHENTICATION (object)));
+			return;
+
+		case PROP_PORT:
+			g_value_set_uint (
+				value,
+				e_source_authentication_get_port (
+				E_SOURCE_AUTHENTICATION (object)));
+			return;
+
+		case PROP_USER:
+			g_value_take_string (
+				value,
+				e_source_authentication_dup_user (
+				E_SOURCE_AUTHENTICATION (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_authentication_finalize (GObject *object)
+{
+	ESourceAuthenticationPrivate *priv;
+
+	priv = E_SOURCE_AUTHENTICATION_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->host);
+	g_free (priv->method);
+	g_free (priv->user);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_authentication_parent_class)->finalize (object);
+}
+
+static void
+e_source_authentication_class_init (ESourceAuthenticationClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceAuthenticationPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_authentication_set_property;
+	object_class->get_property = source_authentication_get_property;
+	object_class->finalize = source_authentication_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_AUTHENTICATION;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_HOST,
+		g_param_spec_string (
+			"host",
+			"Host",
+			"Host name for the remote account",
+			"",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_METHOD,
+		g_param_spec_string (
+			"method",
+			"Method",
+			"Authentication method",
+			"none",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PORT,
+		g_param_spec_uint (
+			"port",
+			"Port",
+			"Port number for the remote account",
+			0, G_MAXUINT16, 0,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_USER,
+		g_param_spec_string (
+			"user",
+			"User",
+			"User name for the remote account",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_authentication_init (ESourceAuthentication *extension)
+{
+	extension->priv = E_SOURCE_AUTHENTICATION_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_authentication_required:
+ * @extension: an #ESourceAuthentication
+ *
+ * This is a convenience function which returns whether authentication
+ * is required at all, regardless of the method used.  This relies on
+ * the convention of setting #ESourceAuthentication:method to "none"
+ * when authentication is <emphasis>not</emphasis> required.
+ *
+ * Returns: whether authentication is required at all
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_authentication_required (ESourceAuthentication *extension)
+{
+	const gchar *method;
+
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), FALSE);
+
+	method = e_source_authentication_get_method (extension);
+	g_return_val_if_fail (method != NULL && *method != '\0', FALSE);
+
+	return (g_strcmp0 (method, "none") != 0);
+}
+
+/**
+ * e_source_authentication_get_host:
+ * @extension: an #ESourceAuthentication
+ *
+ * Returns the host name used to authenticate to a remote account.
+ *
+ * Returns: the host name of a remote account
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_authentication_get_host (ESourceAuthentication *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+	return extension->priv->host;
+}
+
+/**
+ * e_source_authentication_dup_host:
+ * @extension: an #ESourceAuthentication
+ *
+ * Thread-safe variation of e_source_authentication_get_host().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceAuthentication:host
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_authentication_dup_host (ESourceAuthentication *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_authentication_get_host (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_authentication_set_host:
+ * @extension: an #ESourceAuthentication
+ * @host: a host name, or %NULL
+ *
+ * Sets the host name used to authenticate to a remote account.
+ *
+ * The internal copy of @host is automatically stripped of leading and
+ * trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_authentication_set_host (ESourceAuthentication *extension,
+                                  const gchar *host)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (host);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->host);
+	extension->priv->host = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "host");
+}
+
+/**
+ * e_source_authentication_get_method:
+ * @extension: an #ESourceAuthentication
+ *
+ * Returns the authentication method for a remote account.  There are
+ * no pre-defined method names; backends are free to set this however
+ * they wish.  If authentication is not required for a remote account,
+ * the convention is to set #ESourceAuthentication:method to "none".
+ *
+ * Returns: the authentication method for a remote account
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_authentication_get_method (ESourceAuthentication *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+	return extension->priv->method;
+}
+
+/**
+ * e_source_authentication_dup_method:
+ * @extension: an #ESourceAuthentication
+ *
+ * Thread-safe variation of e_source_authentication_get_method().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceAuthentication:method
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_authentication_dup_method (ESourceAuthentication *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_authentication_get_method (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_authentication_set_method:
+ * @extension: an #ESourceAuthentication
+ * @method: authentication method, or %NULL
+ *
+ * Sets the authentication method for a remote account.  There are no
+ * pre-defined method names; backends are free to set this however they
+ * wish.  If authentication is not required for a remote account, the
+ * convention is to set the method to "none".  In keeping with that
+ * convention, #ESourceAuthentication:method will be set to "none" if
+ * @method is %NULL or an empty string.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_authentication_set_method (ESourceAuthentication *extension,
+                                    const gchar *method)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (method);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	/* Convert NULL to "none". */
+	if (duplicate == NULL)
+		duplicate = g_strdup ("none");
+
+	g_free (extension->priv->method);
+	extension->priv->method = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "method");
+}
+
+/**
+ * e_source_authentication_get_port:
+ * @extension: an #ESourceAuthentication
+ *
+ * Returns the port number used to authenticate to a remote account.
+ *
+ * Returns: the port number of a remote account
+ *
+ * Since: 3.6
+ **/
+guint16
+e_source_authentication_get_port (ESourceAuthentication *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), 0);
+
+	return extension->priv->port;
+}
+
+/**
+ * e_source_authentication_set_port:
+ * @extension: an #ESourceAuthentication
+ * @port: a port number
+ *
+ * Sets the port number used to authenticate to a remote account.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_authentication_set_port (ESourceAuthentication *extension,
+                                  guint16 port)
+{
+	g_return_if_fail (E_SOURCE_AUTHENTICATION (extension));
+
+	extension->priv->port = port;
+
+	g_object_notify (G_OBJECT (extension), "port");
+}
+
+/**
+ * e_source_authentication_get_user:
+ * @extension: an #ESourceAuthentication
+ *
+ * Returns the user name used to authenticate to a remote account.
+ *
+ * Returns: the user name of a remote account
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_authentication_get_user (ESourceAuthentication *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+	return extension->priv->user;
+}
+
+/**
+ * e_source_authentication_dup_user:
+ * @extension: an #ESourceAuthentication
+ *
+ * Thread-safe variation of e_source_authentication_get_user().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceAuthentication:user
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_authentication_dup_user (ESourceAuthentication *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_authentication_get_user (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_authentication_set_user:
+ * @extension: an #ESourceAuthentication
+ * @user: a user name, or %NULL
+ *
+ * Sets the user name used to authenticate to a remote account.
+ *
+ * The internal copy of @user is automatically stripped of leading and
+ * trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_authentication_set_user (ESourceAuthentication *extension,
+                                  const gchar *user)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (user);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->user);
+	extension->priv->user = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "user");
+}
diff --git a/libedataserver/e-source-authentication.h b/libedataserver/e-source-authentication.h
new file mode 100644
index 0000000..86faaf0
--- /dev/null
+++ b/libedataserver/e-source-authentication.h
@@ -0,0 +1,109 @@
+/*
+ * e-source-authentication.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_SOURCE_AUTHENTICATION_H
+#define E_SOURCE_AUTHENTICATION_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_AUTHENTICATION \
+	(e_source_authentication_get_type ())
+#define E_SOURCE_AUTHENTICATION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthentication))
+#define E_SOURCE_AUTHENTICATION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthenticationClass))
+#define E_IS_SOURCE_AUTHENTICATION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_AUTHENTICATION))
+#define E_IS_SOURCE_AUTHENTICATION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_AUTHENTICATION))
+#define E_SOURCE_AUTHENTICATION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthenticationClass))
+
+/**
+ * E_SOURCE_EXTENSION_AUTHENTICATION:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceAuthentication.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_AUTHENTICATION "Authentication"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceAuthentication ESourceAuthentication;
+typedef struct _ESourceAuthenticationClass ESourceAuthenticationClass;
+typedef struct _ESourceAuthenticationPrivate ESourceAuthenticationPrivate;
+
+/**
+ * ESourceAuthentication:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceAuthentication {
+	ESourceExtension parent;
+	ESourceAuthenticationPrivate *priv;
+};
+
+struct _ESourceAuthenticationClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_authentication_get_type
+					(void) G_GNUC_CONST;
+gboolean	e_source_authentication_required
+					(ESourceAuthentication *extension);
+const gchar *	e_source_authentication_get_host
+					(ESourceAuthentication *extension);
+gchar *		e_source_authentication_dup_host
+					(ESourceAuthentication *extension);
+void		e_source_authentication_set_host
+					(ESourceAuthentication *extension,
+					 const gchar *host);
+const gchar *	e_source_authentication_get_method
+					(ESourceAuthentication *extension);
+gchar *		e_source_authentication_dup_method
+					(ESourceAuthentication *extension);
+void		e_source_authentication_set_method
+					(ESourceAuthentication *extension,
+					 const gchar *method);
+guint16		e_source_authentication_get_port
+					(ESourceAuthentication *extension);
+void		e_source_authentication_set_port
+					(ESourceAuthentication *extension,
+					 guint16 port);
+const gchar *	e_source_authentication_get_user
+					(ESourceAuthentication *extension);
+gchar *		e_source_authentication_dup_user
+					(ESourceAuthentication *extension);
+void		e_source_authentication_set_user
+					(ESourceAuthentication *extension,
+					 const gchar *user);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_AUTHENTICATION_H */
diff --git a/libedataserver/e-source-authenticator.c b/libedataserver/e-source-authenticator.c
new file mode 100644
index 0000000..2d90c35
--- /dev/null
+++ b/libedataserver/e-source-authenticator.c
@@ -0,0 +1,496 @@
+/*
+ * e-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/>
+ *
+ */
+
+/**
+ * SECTION: e-source-authenticator
+ * @include: libedataserver/e-source-authenticator.h
+ * @short_description: Interface for authentication attempts
+ *
+ * An object implementing the #ESourceAuthenticator interface gets passed
+ * to e_source_registry_authenticate().  The job of an #ESourceAuthenticator
+ * is to test whether a remote server will accept a given password, and then
+ * indicate the result by returning an #ESourceAuthenticationResult value.
+ *
+ * Typically only #EBackend subclasses need to implement this interface,
+ * as client applications are not involved in authentication.
+ *
+ * Note this interface is designed around "stateful authentication", where
+ * one connects to a server, provides credentials for authentication once,
+ * and then issues commands in an authenticated state for the remainder of
+ * the session.
+ *
+ * Backends requiring "stateless authentication" -- where credentials are
+ * included with each command -- will typically want to cache the password
+ * internally once it's verified as part of implementing this interface.
+ **/
+
+#include "e-source-authenticator.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+/* These are for building an authentication prompt. */
+#include <libedataserver/e-source-address-book.h>
+#include <libedataserver/e-source-authentication.h>
+#include <libedataserver/e-source-calendar.h>
+#include <libedataserver/e-source-collection.h>
+#include <libedataserver/e-source-mail-account.h>
+#include <libedataserver/e-source-mail-identity.h>
+#include <libedataserver/e-source-mail-transport.h>
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+	GString *password;
+	ESourceAuthenticationResult result;
+};
+
+G_DEFINE_INTERFACE (
+	ESourceAuthenticator,
+	e_source_authenticator,
+	G_TYPE_OBJECT)
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+	g_string_free (async_context->password, TRUE);
+
+	g_slice_free (AsyncContext, async_context);
+}
+
+static void
+source_authenticator_get_prompt_strings (ESourceAuthenticator *auth,
+                                         ESource *source,
+                                         gchar **prompt_title,
+                                         gchar **prompt_message,
+                                         gchar **prompt_description)
+{
+	ESourceAuthentication *extension;
+	GString *description;
+	const gchar *message;
+	const gchar *extension_name;
+	gchar *display_name;
+	gchar *host_name;
+	gchar *user_name;
+
+	/* Known types */
+	enum {
+		TYPE_UNKNOWN,
+		TYPE_AMBIGUOUS,
+		TYPE_ADDRESS_BOOK,
+		TYPE_CALENDAR,
+		TYPE_MAIL_ACCOUNT,
+		TYPE_MAIL_TRANSPORT,
+		TYPE_MEMO_LIST,
+		TYPE_TASK_LIST
+	} type = TYPE_UNKNOWN;
+
+	/* 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". */
+
+	display_name = e_source_dup_display_name (source);
+
+	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+	extension = e_source_get_extension (source, extension_name);
+	host_name = e_source_authentication_dup_host (extension);
+	user_name = e_source_authentication_dup_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_MAIL_TRANSPORT;
+	if (e_source_has_extension (source, extension_name)) {
+		if (type == TYPE_UNKNOWN)
+			type = TYPE_MAIL_TRANSPORT;
+		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;
+	}
+
+	switch (type) {
+		case TYPE_ADDRESS_BOOK:
+			message = _("Address book authentication request");
+			break;
+		case TYPE_CALENDAR:
+		case TYPE_MEMO_LIST:
+		case TYPE_TASK_LIST:
+			message = _("Calendar authentication request");
+			break;
+		case TYPE_MAIL_ACCOUNT:
+		case TYPE_MAIL_TRANSPORT:
+			message = _("Mail authentication request");
+			break;
+		default:  /* generic account prompt */
+			message = _("Authentication request");
+			break;
+	}
+
+	description = g_string_sized_new (256);
+
+	switch (type) {
+		case TYPE_ADDRESS_BOOK:
+			g_string_append_printf (
+				description,
+				_("Please enter the password for "
+				  "address book \"%s\"."), display_name);
+			break;
+		case TYPE_CALENDAR:
+			g_string_append_printf (
+				description,
+				_("Please enter the password for "
+				  "calendar \"%s\"."), display_name);
+			break;
+		case TYPE_MAIL_ACCOUNT:
+			g_string_append_printf (
+				description,
+				_("Please enter the password for "
+				  "mail account \"%s\"."), display_name);
+			break;
+		case TYPE_MAIL_TRANSPORT:
+			g_string_append_printf (
+				description,
+				_("Please enter the password for "
+				  "mail transport \"%s\"."), display_name);
+			break;
+		case TYPE_MEMO_LIST:
+			g_string_append_printf (
+				description,
+				_("Please enter the password for "
+				  "memo list \"%s\"."), display_name);
+			break;
+		case TYPE_TASK_LIST:
+			g_string_append_printf (
+				description,
+				_("Please enter the password for "
+				  "task list \"%s\"."), display_name);
+			break;
+		default:  /* generic account prompt */
+			g_string_append_printf (
+				description,
+				_("Please enter the password for "
+				  "account \"%s\"."), display_name);
+			break;
+	}
+
+	if (host_name != NULL && user_name != NULL)
+		g_string_append_printf (
+			description, "\n(user: %s, host: %s)",
+			user_name, host_name);
+	else if (host_name != NULL)
+		g_string_append_printf (
+			description, "\n(host: %s)", host_name);
+	else if (user_name != NULL)
+		g_string_append_printf (
+			description, "\n(user: %s)", user_name);
+
+	*prompt_title = g_strdup ("");
+	*prompt_message = g_strdup (message);
+	*prompt_description = g_string_free (description, FALSE);
+
+	g_free (display_name);
+	g_free (host_name);
+	g_free (user_name);
+}
+
+/* Helper for source_authenticator_try_password() */
+static void
+source_authenticator_try_password_thread (GSimpleAsyncResult *simple,
+                                          GObject *object,
+                                          GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	async_context->result =
+		e_source_authenticator_try_password_sync (
+			E_SOURCE_AUTHENTICATOR (object),
+			async_context->password,
+			cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+static void
+source_authenticator_try_password (ESourceAuthenticator *auth,
+                                   const GString *password,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->password = g_string_new (password->str);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (auth), callback, user_data,
+		source_authenticator_try_password);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_authenticator_try_password_thread,
+		G_PRIORITY_DEFAULT, cancellable);
+
+	g_object_unref (simple);
+}
+
+static ESourceAuthenticationResult
+source_authenticator_try_password_finish (ESourceAuthenticator *auth,
+                                          GAsyncResult *result,
+                                          GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (auth),
+		source_authenticator_try_password),
+		E_SOURCE_AUTHENTICATION_REJECTED);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return E_SOURCE_AUTHENTICATION_ERROR;
+
+	return async_context->result;
+}
+
+static void
+e_source_authenticator_default_init (ESourceAuthenticatorInterface *interface)
+{
+	interface->get_prompt_strings = source_authenticator_get_prompt_strings;
+	interface->try_password = source_authenticator_try_password;
+	interface->try_password_finish = source_authenticator_try_password_finish;
+}
+
+/**
+ * e_source_authenticator_get_prompt_strings:
+ * @auth: an #ESourceAuthenticator
+ * @source: an #ESource
+ * @prompt_title: (out): the title of the prompt
+ * @prompt_message: (out): the prompt message for the user
+ * @prompt_description: (out): the detailed description of the prompt
+ *
+ * Generates authentication prompt strings for @source.
+ *
+ * For registry service clients, #ESourceRegistry calls this function as
+ * part of e_source_registry_authenticate_sync().  In the registry service
+ * itself, #EAuthenticationSession calls this function during initialization.
+ * This function should rarely need to be called explicitly outside of those
+ * two cases.
+ *
+ * The #ESourceAuthenticatorInterface defines a default behavior for this
+ * method which should suffice in most cases.  But implementors can still
+ * override the method if needed for special circumstances.
+ *
+ * Free each of the returned prompt strings with g_free().
+ *
+ * Since: 3.6
+ **/
+void
+e_source_authenticator_get_prompt_strings (ESourceAuthenticator *auth,
+                                           ESource *source,
+                                           gchar **prompt_title,
+                                           gchar **prompt_message,
+                                           gchar **prompt_description)
+{
+	ESourceAuthenticatorInterface *interface;
+
+	g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (prompt_title != NULL);
+	g_return_if_fail (prompt_message != NULL);
+	g_return_if_fail (prompt_description != NULL);
+
+	interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
+	g_return_if_fail (interface->get_prompt_strings);
+
+	interface->get_prompt_strings (
+		auth, source,
+		prompt_title,
+		prompt_message,
+		prompt_description);
+}
+
+/**
+ * e_source_authenticator_try_password_sync:
+ * @auth: an #ESourceAuthenticator
+ * @password: a user-provided password
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Attempts to authenticate using @password.
+ *
+ * The password is passed in a #GString container so its content is not
+ * accidentally revealed in a stack trace.
+ *
+ * If an error occurs, the function sets @error and returns
+ * #E_SOURCE_AUTHENTICATION_ERROR.
+ *
+ * Returns: the authentication result
+ *
+ * Since: 3.6
+ **/
+ESourceAuthenticationResult
+e_source_authenticator_try_password_sync (ESourceAuthenticator *auth,
+                                          const GString *password,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+	ESourceAuthenticatorInterface *interface;
+
+	g_return_val_if_fail (
+		E_IS_SOURCE_AUTHENTICATOR (auth),
+		E_SOURCE_AUTHENTICATION_REJECTED);
+	g_return_val_if_fail (
+		password != NULL,
+		E_SOURCE_AUTHENTICATION_REJECTED);
+
+	interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
+	g_return_val_if_fail (
+		interface->try_password_sync != NULL,
+		E_SOURCE_AUTHENTICATION_REJECTED);
+
+	return interface->try_password_sync (
+		auth, password, cancellable, error);
+}
+
+/**
+ * e_source_authenticator_try_password:
+ * @auth: an #ESourceAuthenticator
+ * @password: a user-provided password
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asyncrhonously attempts to authenticate using @password.
+ *
+ * The password is passed in a #GString container so its content is not
+ * accidentally revealed in a stack trace.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_authenticator_try_password_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_authenticator_try_password (ESourceAuthenticator *auth,
+                                     const GString *password,
+                                     GCancellable *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+	ESourceAuthenticatorInterface *interface;
+
+	g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
+	g_return_if_fail (password != NULL);
+
+	interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
+	g_return_if_fail (interface->try_password != NULL);
+
+	interface->try_password (
+		auth, password, cancellable, callback, user_data);
+}
+
+/**
+ * e_source_authenticator_try_password_finish:
+ * @auth: an #ESourceAuthenticator
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_authenticator_try_password().
+ *
+ * If an error occurred, the function sets @error and returns
+ * #E_SOURCE_AUTHENTICATION_ERROR.
+ *
+ * Returns: the authentication result
+ *
+ * Since: 3.6
+ **/
+ESourceAuthenticationResult
+e_source_authenticator_try_password_finish (ESourceAuthenticator *auth,
+                                            GAsyncResult *result,
+                                            GError **error)
+{
+	ESourceAuthenticatorInterface *interface;
+
+	g_return_val_if_fail (
+		E_IS_SOURCE_AUTHENTICATOR (auth),
+		E_SOURCE_AUTHENTICATION_REJECTED);
+	g_return_val_if_fail (
+		G_IS_ASYNC_RESULT (result),
+		E_SOURCE_AUTHENTICATION_REJECTED);
+
+	interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
+	g_return_val_if_fail (
+		interface->try_password_finish != NULL,
+		E_SOURCE_AUTHENTICATION_REJECTED);
+
+	return interface->try_password_finish (auth, result, error);
+}
+
diff --git a/libedataserver/e-source-authenticator.h b/libedataserver/e-source-authenticator.h
new file mode 100644
index 0000000..c40d09b
--- /dev/null
+++ b/libedataserver/e-source-authenticator.h
@@ -0,0 +1,103 @@
+/*
+ * e-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_SOURCE_AUTHENTICATOR_H
+#define E_SOURCE_AUTHENTICATOR_H
+
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-enums.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_AUTHENTICATOR \
+	(e_source_authenticator_get_type ())
+#define E_SOURCE_AUTHENTICATOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_AUTHENTICATOR, ESourceAuthenticator))
+#define E_IS_SOURCE_AUTHENTICATOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_AUTHENTICATOR))
+#define E_SOURCE_AUTHENTICATOR_GET_INTERFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE \
+	((obj), E_TYPE_SOURCE_AUTHENTICATOR, ESourceAuthenticatorInterface))
+
+G_BEGIN_DECLS
+
+/**
+ * ESourceAuthenticator:
+ *
+ * Since: 3.6
+ **/
+typedef struct _ESourceAuthenticator ESourceAuthenticator;
+typedef struct _ESourceAuthenticatorInterface ESourceAuthenticatorInterface;
+
+struct _ESourceAuthenticatorInterface {
+	GTypeInterface parent_interface;
+
+	void		(*get_prompt_strings)	(ESourceAuthenticator *auth,
+						 ESource *source,
+						 gchar **prompt_title,
+						 gchar **prompt_message,
+						 gchar **prompt_description);
+
+	/* Synchronous I/O Methods */
+	ESourceAuthenticationResult
+			(*try_password_sync)	(ESourceAuthenticator *auth,
+						 const GString *password,
+						 GCancellable *cancellable,
+						 GError **error);
+
+	/* Asynchronous I/O Methods (all have defaults) */
+	void		(*try_password)		(ESourceAuthenticator *auth,
+						 const GString *password,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+	ESourceAuthenticationResult
+			(*try_password_finish)	(ESourceAuthenticator *auth,
+						 GAsyncResult *result,
+						 GError **error);
+};
+
+GType		e_source_authenticator_get_type	(void) G_GNUC_CONST;
+void		e_source_authenticator_get_prompt_strings
+						(ESourceAuthenticator *auth,
+						 ESource *source,
+						 gchar **prompt_title,
+						 gchar **prompt_message,
+						 gchar **prompt_description);
+ESourceAuthenticationResult
+		e_source_authenticator_try_password_sync
+						(ESourceAuthenticator *auth,
+						 const GString *password,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_source_authenticator_try_password
+						(ESourceAuthenticator *auth,
+						 const GString *password,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+ESourceAuthenticationResult
+		e_source_authenticator_try_password_finish
+						(ESourceAuthenticator *auth,
+						 GAsyncResult *result,
+						 GError **error);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_AUTHENTICATOR_H */
diff --git a/libedataserver/e-source-autocomplete.c b/libedataserver/e-source-autocomplete.c
new file mode 100644
index 0000000..5aa9d13
--- /dev/null
+++ b/libedataserver/e-source-autocomplete.c
@@ -0,0 +1,168 @@
+/*
+ * e-source-autocomplete.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-autocomplete
+ * @include: libedataserver/e-source-autocomplete.h
+ * @short_description: #ESource extension for autocomplete settings
+ *
+ * The #ESourceAutocomplete extension tracks contact autocompletion
+ * settings for an address book.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-autocomplete.h>
+ *
+ *   ESourceAutocomplete *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTOCOMPLETE);
+ * ]|
+ **/
+
+#include "e-source-autocomplete.h"
+
+#define E_SOURCE_AUTOCOMPLETE_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocompletePrivate))
+
+struct _ESourceAutocompletePrivate {
+	gboolean include_me;
+};
+
+enum {
+	PROP_0,
+	PROP_INCLUDE_ME
+};
+
+G_DEFINE_TYPE (
+	ESourceAutocomplete,
+	e_source_autocomplete,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_autocomplete_set_property (GObject *object,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_INCLUDE_ME:
+			e_source_autocomplete_set_include_me (
+				E_SOURCE_AUTOCOMPLETE (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_autocomplete_get_property (GObject *object,
+                                    guint property_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_INCLUDE_ME:
+			g_value_set_boolean (
+				value,
+				e_source_autocomplete_get_include_me (
+				E_SOURCE_AUTOCOMPLETE (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_source_autocomplete_class_init (ESourceAutocompleteClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceAutocompletePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_autocomplete_set_property;
+	object_class->get_property = source_autocomplete_get_property;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_AUTOCOMPLETE;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_INCLUDE_ME,
+		g_param_spec_boolean (
+			"include-me",
+			"IncludeMe",
+			"Include this source when autocompleting",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_autocomplete_init (ESourceAutocomplete *extension)
+{
+	extension->priv = E_SOURCE_AUTOCOMPLETE_GET_PRIVATE (extension);
+}
+
+/**
+ * e_source_autocomplete_get_include_me:
+ * @extension: an #ESourceAutocomplete
+ *
+ * Returns whether the address book described by the #ESource to which
+ * @extension belongs should be queried when the user inputs a partial
+ * contact name or email address.
+ *
+ * Returns: whether to use the autocomplete feature
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_autocomplete_get_include_me (ESourceAutocomplete *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_AUTOCOMPLETE (extension), FALSE);
+
+	return extension->priv->include_me;
+}
+
+/**
+ * e_source_autocomplete_set_include_me:
+ * @extension: an #ESourceAutocomplete
+ * @include_me: whether to use the autocomplete feature
+ *
+ * Sets whether the address book described by the #ESource to which
+ * @extension belongs should be queried when the user inputs a partial
+ * contact name or email address.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_autocomplete_set_include_me (ESourceAutocomplete *extension,
+                                      gboolean include_me)
+{
+	g_return_if_fail (E_IS_SOURCE_AUTOCOMPLETE (extension));
+
+	extension->priv->include_me = include_me;
+
+	g_object_notify (G_OBJECT (extension), "include-me");
+}
diff --git a/libedataserver/e-source-autocomplete.h b/libedataserver/e-source-autocomplete.h
new file mode 100644
index 0000000..db42606
--- /dev/null
+++ b/libedataserver/e-source-autocomplete.h
@@ -0,0 +1,86 @@
+/*
+ * e-source-autocomplete.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_SOURCE_AUTOCOMPLETE_H
+#define E_SOURCE_AUTOCOMPLETE_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_AUTOCOMPLETE \
+	(e_source_autocomplete_get_type ())
+#define E_SOURCE_AUTOCOMPLETE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocomplete))
+#define E_SOURCE_AUTOCOMPLETE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocompleteClass))
+#define E_IS_SOURCE_AUTOCOMPLETE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_AUTOCOMPLETE))
+#define E_IS_SOURCE_AUTOCOMPLETE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_AUTOCOMPLETE))
+#define E_SOURCE_AUTOCOMPLETE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocompleteClass))
+
+/**
+ * E_SOURCE_EXTENSION_AUTOCOMPLETE:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceAutocomplete.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_AUTOCOMPLETE "Autocomplete"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceAutocomplete ESourceAutocomplete;
+typedef struct _ESourceAutocompleteClass ESourceAutocompleteClass;
+typedef struct _ESourceAutocompletePrivate ESourceAutocompletePrivate;
+
+/**
+ * ESourceAutocomplete:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceAutocomplete {
+	ESourceExtension parent;
+	ESourceAutocompletePrivate *priv;
+};
+
+struct _ESourceAutocompleteClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_autocomplete_get_type
+					(void) G_GNUC_CONST;
+gboolean	e_source_autocomplete_get_include_me
+					(ESourceAutocomplete *extension);
+void		e_source_autocomplete_set_include_me
+					(ESourceAutocomplete *extension,
+					 gboolean include_me);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_AUTOCOMPLETE_H */
diff --git a/libedataserver/e-source-backend.c b/libedataserver/e-source-backend.c
new file mode 100644
index 0000000..9b3c311
--- /dev/null
+++ b/libedataserver/e-source-backend.c
@@ -0,0 +1,226 @@
+/*
+ * e-source-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-backend
+ * @include: libedataserver/e-source-backend.h
+ * @short_description: Base class for backend-based data sources
+ *
+ * #ESourceBackend is an abstract base class for data sources requiring
+ * an associated backend to function.  The extension merely records the
+ * name of the backend the data source should be paired with.
+ **/
+
+#include "e-source-backend.h"
+
+#define E_SOURCE_BACKEND_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_BACKEND, ESourceBackendPrivate))
+
+struct _ESourceBackendPrivate {
+	GMutex *property_lock;
+	gchar *backend_name;
+};
+
+enum {
+	PROP_0,
+	PROP_BACKEND_NAME
+};
+
+G_DEFINE_ABSTRACT_TYPE (
+	ESourceBackend,
+	e_source_backend,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_backend_set_property (GObject *object,
+                             guint property_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_BACKEND_NAME:
+			e_source_backend_set_backend_name (
+				E_SOURCE_BACKEND (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_backend_get_property (GObject *object,
+                             guint property_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_BACKEND_NAME:
+			g_value_take_string (
+				value,
+				e_source_backend_dup_backend_name (
+				E_SOURCE_BACKEND (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_backend_finalize (GObject *object)
+{
+	ESourceBackendPrivate *priv;
+
+	priv = E_SOURCE_BACKEND_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->backend_name);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_backend_parent_class)->finalize (object);
+}
+
+static void
+e_source_backend_class_init (ESourceBackendClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESourceBackendPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_backend_set_property;
+	object_class->get_property = source_backend_get_property;
+	object_class->finalize = source_backend_finalize;
+
+	/* We do not provide an extension name,
+	 * which is why the class is abstract. */
+
+	g_object_class_install_property (
+		object_class,
+		PROP_BACKEND_NAME,
+		g_param_spec_string (
+			"backend-name",
+			"Backend Name",
+			"The name of the backend handling the data source",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_backend_init (ESourceBackend *extension)
+{
+	extension->priv = E_SOURCE_BACKEND_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_backend_get_backend_name:
+ * @extension: an #ESourceBackend
+ *
+ * Returns the backend name for @extension.
+ *
+ * Returns: the backend name for @extension
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_backend_get_backend_name (ESourceBackend *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_BACKEND (extension), NULL);
+
+	return extension->priv->backend_name;
+}
+
+/**
+ * e_source_backend_dup_backend_name:
+ * @extension: an #ESourceBackend
+ *
+ * Thread-safe variation of e_source_backend_get_backend_name().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceBackend:backend-name
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_backend_dup_backend_name (ESourceBackend *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_BACKEND (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_backend_get_backend_name (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_backend_set_backend_name:
+ * @extension: an #ESourceBackend
+ * @backend_name: a backend name
+ *
+ * Sets the backend name for @extension.
+ *
+ * The internal copy of @backend_name is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_backend_set_backend_name (ESourceBackend *extension,
+                                   const gchar *backend_name)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_BACKEND (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (backend_name);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->backend_name);
+	extension->priv->backend_name = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "backend-name");
+}
+
diff --git a/libedataserver/e-source-backend.h b/libedataserver/e-source-backend.h
new file mode 100644
index 0000000..5d3baee
--- /dev/null
+++ b/libedataserver/e-source-backend.h
@@ -0,0 +1,77 @@
+/*
+ * e-source-backend.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_SOURCE_BACKEND_H
+#define E_SOURCE_BACKEND_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_BACKEND \
+	(e_source_backend_get_type ())
+#define E_SOURCE_BACKEND(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_BACKEND, ESourceBackend))
+#define E_SOURCE_BACKEND_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_BACKEND, ESourceBackendClass))
+#define E_IS_SOURCE_BACKEND(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_BACKEND))
+#define E_IS_SOURCE_BACKEND_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_BACKEND))
+#define E_SOURCE_BACKEND_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_BACKEND, ESourceBackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceBackend ESourceBackend;
+typedef struct _ESourceBackendClass ESourceBackendClass;
+typedef struct _ESourceBackendPrivate ESourceBackendPrivate;
+
+/**
+ * ESourceBackend:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceBackend {
+	ESourceExtension parent;
+	ESourceBackendPrivate *priv;
+};
+
+struct _ESourceBackendClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_backend_get_type	(void) G_GNUC_CONST;
+const gchar *	e_source_backend_get_backend_name
+						(ESourceBackend *extension);
+gchar *		e_source_backend_dup_backend_name
+						(ESourceBackend *extension);
+void		e_source_backend_set_backend_name
+						(ESourceBackend *extension,
+						 const gchar *backend_name);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_BACKEND_H */
diff --git a/libedataserver/e-source-calendar.c b/libedataserver/e-source-calendar.c
new file mode 100644
index 0000000..13d0d52
--- /dev/null
+++ b/libedataserver/e-source-calendar.c
@@ -0,0 +1,135 @@
+/*
+ * e-source-calendar.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/>
+ *
+ */
+
+/* FIXME: Break these into separate files after libedataserver
+ *        moves to a single include paradigm. */
+
+/**
+ * SECTION: e-source-calendar
+ * @include: libedataserver/e-source-calendar.h
+ * @short_description: #ESource extension for a calendar
+ *
+ * The #ESourceCalendar extension identifies the #ESource as a calendar.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-calendar.h>
+ *
+ *   ESourceCalendar *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
+ * ]|
+ **/
+
+/**
+ * SECTION: e-source-memo-list
+ * @include: libedataserver/e-source-calendar.h
+ * @short_description: #ESource extension for a memo list
+ *
+ * The #ESourceCalendar extension identifies the #ESource as a memo list.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-calendar.h>
+ *
+ *   ESourceCalendar *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST);
+ * ]|
+ **/
+
+/**
+ * SECTION: e-source-task-list
+ * @include: libedataserver/e-source-calendar.h
+ * @short_description: #ESource extension for a task list
+ *
+ * The #ESourceCalendar extension identifies the #ESource as a task list.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-calendar.h>
+ *
+ *   ESourceCalendar *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
+ * ]|
+ **/
+
+#include "e-source-calendar.h"
+
+#include <libedataserver/e-data-server-util.h>
+
+G_DEFINE_TYPE (
+	ESourceCalendar,
+	e_source_calendar,
+	E_TYPE_SOURCE_SELECTABLE)
+
+G_DEFINE_TYPE (
+	ESourceMemoList,
+	e_source_memo_list,
+	E_TYPE_SOURCE_SELECTABLE)
+
+G_DEFINE_TYPE (
+	ESourceTaskList,
+	e_source_task_list,
+	E_TYPE_SOURCE_SELECTABLE)
+
+static void
+e_source_calendar_class_init (ESourceCalendarClass *class)
+{
+	ESourceExtensionClass *extension_class;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_CALENDAR;
+}
+
+static void
+e_source_calendar_init (ESourceCalendar *extension)
+{
+}
+
+static void
+e_source_memo_list_class_init (ESourceMemoListClass *class)
+{
+	ESourceExtensionClass *extension_class;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MEMO_LIST;
+}
+
+static void
+e_source_memo_list_init (ESourceMemoList *extension)
+{
+}
+
+static void
+e_source_task_list_class_init (ESourceTaskListClass *class)
+{
+	ESourceExtensionClass *extension_class;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_TASK_LIST;
+}
+
+static void
+e_source_task_list_init (ESourceTaskList *extension)
+{
+}
diff --git a/libedataserver/e-source-calendar.h b/libedataserver/e-source-calendar.h
new file mode 100644
index 0000000..ac84bc4
--- /dev/null
+++ b/libedataserver/e-source-calendar.h
@@ -0,0 +1,185 @@
+/*
+ * e-source-calendar.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_SOURCE_CALENDAR_H
+#define E_SOURCE_CALENDAR_H
+
+/* These are trivial but important ESourceSelectable subclasses.
+ * They identify an ESource as a calendar, memo list or task list. */
+
+#include <libedataserver/e-source-selectable.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_CALENDAR \
+	(e_source_calendar_get_type ())
+#define E_SOURCE_CALENDAR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_CALENDAR, ESourceCalendar))
+#define E_SOURCE_CALENDAR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_CALENDAR, ESourceCalendarClass))
+#define E_IS_SOURCE_CALENDAR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_CALENDAR))
+#define E_IS_SOURCE_CALENDAR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_CALENDAR))
+#define E_SOURCE_CALENDAR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_CALENDAR, ESourceCalendarClass))
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MEMO_LIST \
+	(e_source_memo_list_get_type ())
+#define E_SOURCE_MEMO_LIST(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MEMO_LIST, ESourceMemoList))
+#define E_SOURCE_MEMO_LIST_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MEMO_LIST, ESourceMemoListClass))
+#define E_IS_SOURCE_MEMO_LIST(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MEMO_LIST))
+#define E_IS_SOURCE_MEMO_LIST_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MEMO_LIST))
+#define E_SOURCE_MEMO_LIST_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MEMO_LIST, ESourceMemoListClass))
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_TASK_LIST \
+	(e_source_task_list_get_type ())
+#define E_SOURCE_TASK_LIST(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_TASK_LIST, ESourceTaskList))
+#define E_SOURCE_TASK_LIST_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_TASK_LIST, ESourceTaskListClass))
+#define E_IS_SOURCE_TASK_LIST(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_TASK_LIST))
+#define E_IS_SOURCE_TASK_LIST_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_TASK_LIST))
+#define E_SOURCE_TASK_LIST_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_TASK_LIST, ESourceTaskListClass))
+
+/**
+ * E_SOURCE_EXTENSION_CALENDAR:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceCalendar.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_CALENDAR  "Calendar"
+
+/**
+ * E_SOURCE_EXTENSION_MEMO_LIST:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMemoList.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MEMO_LIST "Memo List"
+
+/**
+ * E_SOURCE_EXTENSION_TASK_LIST:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceTaskList.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_TASK_LIST "Task List"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCalendar ESourceCalendar;
+typedef struct _ESourceCalendarClass ESourceCalendarClass;
+typedef struct _ESourceCalendarPrivate ESourceCalendarPrivate;
+
+typedef struct _ESourceMemoList ESourceMemoList;
+typedef struct _ESourceMemoListClass ESourceMemoListClass;
+typedef struct _ESourceMemoListPrivate ESourceMemoListPrivate;
+
+typedef struct _ESourceTaskList ESourceTaskList;
+typedef struct _ESourceTaskListClass ESourceTaskListClass;
+typedef struct _ESourceTaskListPrivate ESourceTaskListPrivate;
+
+/**
+ * ESourceCalendar:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceCalendar {
+	ESourceSelectable parent;
+	ESourceCalendarPrivate *priv;
+};
+
+struct _ESourceCalendarClass {
+	ESourceSelectableClass parent_class;
+};
+
+/**
+ * ESourceMemoList:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMemoList {
+	ESourceSelectable parent;
+	ESourceMemoListPrivate *priv;
+};
+
+struct _ESourceMemoListClass {
+	ESourceSelectableClass parent_class;
+};
+
+/**
+ * ESourceTaskList:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceTaskList {
+	ESourceSelectable parent;
+	ESourceTaskListPrivate *priv;
+};
+
+struct _ESourceTaskListClass {
+	ESourceSelectableClass parent_class;
+};
+
+GType		e_source_calendar_get_type	(void);
+GType		e_source_memo_list_get_type	(void);
+GType		e_source_task_list_get_type	(void);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_CALENDAR_H */
diff --git a/libedataserver/e-source-camel.c b/libedataserver/e-source-camel.c
new file mode 100644
index 0000000..df0d67b
--- /dev/null
+++ b/libedataserver/e-source-camel.c
@@ -0,0 +1,738 @@
+/*
+ * e-source-camel-provider.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-camel
+ * @include: libedataserver/e-source-camel.h
+ * @short_description: #ESource extension for #CamelSettings
+ *
+ * #ESourceCamel itself is abstract.  Its sole function is to
+ * bridge #GObject properties from the #CamelSettings framework to the
+ * #ESource framework.  It does this by procedurally registering an
+ * #ESourceCamel subtype for each available #CamelService subtype,
+ * and then registering #GObject properties to proxy the properties in the
+ * corresponding #CamelSettings subtype.  The #ESourceCamel owns an
+ * instance of the appropriate #CamelSettings subtype, and redirects all
+ * get/set operations on its own #GObject properties to its #CamelSettings
+ * instance.  The #CamelSettings instance, now fully initialized from a key
+ * file, can then be inserted into a new #CamelService instance using
+ * camel_service_set_settings().
+ *
+ * Ultimately, this is all just implementation detail for glueing two
+ * unrelated class hierarchies together.  If you need to access provider
+ * specific settings, use the #CamelSettings API, not this.
+ **/
+
+#include "e-source-camel.h"
+
+#include <string.h>
+#include <glib/gprintf.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-source-authentication.h>
+#include <libedataserver/e-source-offline.h>
+#include <libedataserver/e-source-security.h>
+
+#define E_SOURCE_CAMEL_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_CAMEL, ESourceCamelPrivate))
+
+struct _ESourceCamelPrivate {
+	CamelSettings *settings;
+	GArray *value_array;
+};
+
+enum {
+	PROP_0,
+	PROP_SETTINGS
+};
+
+typedef struct {
+	const gchar *extension_name;
+	const gchar *extension_property_name;
+	const gchar *settings_property_name;
+	GBindingTransformFunc extension_to_settings;
+	GBindingTransformFunc settings_to_extension;
+} BindingData;
+
+static gboolean
+transform_none_to_null (GBinding *binding,
+                        const GValue *source_value,
+                        GValue *target_value,
+                        gpointer not_used)
+{
+	const gchar *v_string;
+
+	/* XXX Camel doesn't understand ESource's convention of using
+	 *     "none" to represent no value, instead of NULL or empty
+	 *     strings.  So convert "none" to NULL for Camel. */
+
+	v_string = g_value_get_string (source_value);
+
+	if (g_strcmp0 (v_string, "none") == 0)
+		v_string = NULL;
+
+	g_value_set_string (target_value, v_string);
+
+	return TRUE;
+}
+
+static BindingData bindings[] = {
+
+	{ E_SOURCE_EXTENSION_AUTHENTICATION,
+	  "host", "host" },
+
+	{ E_SOURCE_EXTENSION_AUTHENTICATION,
+	  "method", "auth-mechanism",
+	  transform_none_to_null,
+	  NULL },
+
+	{ E_SOURCE_EXTENSION_AUTHENTICATION,
+	  "port", "port" },
+
+	{ E_SOURCE_EXTENSION_AUTHENTICATION,
+	  "user", "user" },
+
+	{ E_SOURCE_EXTENSION_OFFLINE,
+	  "stay-synchronized", "stay-synchronized" },
+
+	{ E_SOURCE_EXTENSION_SECURITY,
+	  "method", "security-method",
+	  e_binding_transform_enum_nick_to_value,
+	  e_binding_transform_enum_value_to_nick }
+};
+
+G_DEFINE_ABSTRACT_TYPE (
+	ESourceCamel,
+	e_source_camel,
+	E_TYPE_SOURCE_EXTENSION)
+
+/* XXX A function like this belongs in GObject.  I may yet propose it,
+ *     GParamSpecClass still has some reserved slots.  This fiddles with
+ *     GParamSpec fields that are supposed to be private to GObject, but
+ *     I have no other choice.
+ *
+ * XXX Historical note, originally I tried (ab)using override properties
+ *     in ESourceCamel, which redirected to the equivalent CamelSettings
+ *     property.  Seemed to work at first, and I was proud of my clever
+ *     hack, but it turns out g_object_class_list_properties() excludes
+ *     override properties.  So the ESourceCamel properties were being
+ *     skipped in source_load_from_key_file() (e-source.c). */
+static GParamSpec *
+param_spec_clone (GParamSpec *pspec)
+{
+	GParamSpec *clone;
+	GTypeQuery query;
+
+	/* Query the instance size. */
+	g_type_query (G_PARAM_SPEC_TYPE (pspec), &query);
+
+	/* Start with a memcpy()'d buffer. */
+	clone = g_slice_alloc0 (query.instance_size);
+	memcpy (clone, pspec, query.instance_size);
+
+	/* This sort of mimics g_param_spec_init(). */
+
+#define PARAM_FLOATING_FLAG 0x2  /* from gparam.c */
+	g_datalist_set_flags (&clone->qdata, PARAM_FLOATING_FLAG);
+	clone->ref_count = 1;
+
+	/* Clear the owner_type. */
+	clone->owner_type = G_TYPE_INVALID;
+
+	/* Clear the param_id. */
+	clone->param_id = 0;
+
+	/* This sort of mimics g_param_spec_internal(). */
+
+	/* Param name should already be canonicalized and interned. */
+
+	/* Always copy the nickname. */
+	clone->flags &= ~G_PARAM_STATIC_NICK;
+	clone->_nick = g_strdup (g_param_spec_get_nick (pspec));
+
+	/* Always copy the blurb. */
+	clone->flags &= ~G_PARAM_STATIC_BLURB;
+	clone->_blurb = g_strdup (g_param_spec_get_blurb (pspec));
+
+	/* Handle special cases. */
+
+	if (G_IS_PARAM_SPEC_STRING (clone)) {
+		GParamSpecString *clone_s;
+
+		clone_s = (GParamSpecString *) clone;
+		clone_s->default_value = g_strdup (clone_s->default_value);
+	}
+
+	/* Some types we don't handle but shouldn't need to. */
+	g_warn_if_fail (!G_IS_PARAM_SPEC_VALUE_ARRAY (clone));
+	g_warn_if_fail (!G_IS_PARAM_SPEC_OVERRIDE (clone));
+	g_warn_if_fail (!G_IS_PARAM_SPEC_VARIANT (clone));
+
+	return clone;
+}
+
+static gint
+subclass_get_binding_index (GParamSpec *settings_property)
+{
+	gint ii;
+
+	/* Return the index in the bindings list for the given
+	 * CamelSettings property specification, or else -1. */
+
+	for (ii = 0; ii < G_N_ELEMENTS (bindings); ii++) {
+		const gchar *property_name;
+
+		property_name = bindings[ii].settings_property_name;
+		if (g_strcmp0 (settings_property->name, property_name) == 0)
+			return ii;
+	}
+
+	return -1;
+}
+
+static void
+subclass_set_property (GObject *object,
+                       guint property_id,
+                       const GValue *src_value,
+                       GParamSpec *pspec)
+{
+	ESourceCamel *extension;
+	GArray *value_array;
+	GValue *dst_value;
+
+	extension = E_SOURCE_CAMEL (object);
+	value_array = extension->priv->value_array;
+
+	dst_value = &g_array_index (value_array, GValue, property_id - 1);
+	g_value_copy (src_value, dst_value);
+}
+
+static void
+subclass_get_property (GObject *object,
+                       guint property_id,
+                       GValue *dst_value,
+                       GParamSpec *pspec)
+{
+	ESourceCamel *extension;
+	GArray *value_array;
+	GValue *src_value;
+
+	extension = E_SOURCE_CAMEL (object);
+	value_array = extension->priv->value_array;
+
+	src_value = &g_array_index (value_array, GValue, property_id - 1);
+	g_value_copy (src_value, dst_value);
+}
+
+static void
+subclass_class_init (gpointer g_class,
+                     gpointer class_data)
+{
+	GObjectClass *object_class;
+
+	/* e_source_camel_generate_subtype() does all the
+	 * dynamic class initialization.  We just do what static
+	 * initialization we can here. */
+
+	object_class = G_OBJECT_CLASS (g_class);
+	object_class->set_property = subclass_set_property;
+	object_class->get_property = subclass_get_property;
+}
+
+static void
+subclass_instance_init (GTypeInstance *instance,
+                        gpointer g_class)
+{
+	/* Nothing to do here, just makes a handy breakpoint. */
+}
+
+static void
+source_camel_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SETTINGS:
+			g_value_set_object (
+				value,
+				e_source_camel_get_settings (
+				E_SOURCE_CAMEL (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_camel_dispose (GObject *object)
+{
+	ESourceCamelPrivate *priv;
+
+	priv = E_SOURCE_CAMEL_GET_PRIVATE (object);
+
+	if (priv->settings != NULL) {
+		g_object_unref (priv->settings);
+		priv->settings = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_source_camel_parent_class)->dispose (object);
+}
+
+static void
+source_camel_finalize (GObject *object)
+{
+	ESourceCamelPrivate *priv;
+	guint ii;
+
+	priv = E_SOURCE_CAMEL_GET_PRIVATE (object);
+
+	for (ii = 0; ii < priv->value_array->len; ii++)
+		g_value_unset (&g_array_index (priv->value_array, GValue, ii));
+
+	g_array_free (priv->value_array, TRUE);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_camel_parent_class)->finalize (object);
+}
+
+static void
+source_camel_constructed (GObject *object)
+{
+	ESource *source;
+	ESourceCamelClass *class;
+	ESourceCamelPrivate *priv;
+	GObjectClass *settings_class;
+	GParamSpec **properties;
+	guint ii, n_properties;
+	guint array_index = 0;
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_source_camel_parent_class)->
+		constructed (object);
+
+	class = E_SOURCE_CAMEL_GET_CLASS (object);
+	priv = E_SOURCE_CAMEL_GET_PRIVATE (object);
+
+	source = e_source_extension_get_source (E_SOURCE_EXTENSION (object));
+
+	priv->settings = g_object_new (class->settings_type, NULL);
+
+	/* Here we bind all the GObject properties in the newly-created
+	 * CamelSettings instance to either our own identical properties
+	 * or properties in another ESourceExtensions.  The bindings list
+	 * at the top of the file maps out bindings to other extensions. */
+
+	settings_class = G_OBJECT_GET_CLASS (priv->settings);
+
+	properties = g_object_class_list_properties (
+		settings_class, &n_properties);
+
+	/* Allocate more elements than we need, since some CamelSettings
+	 * properties get bound to properties of other ESourceExtensions.
+	 * We'll trim off the extra elements later. */
+	g_array_set_size (priv->value_array, n_properties);
+
+	for (ii = 0; ii < n_properties; ii++) {
+		GParamSpec *pspec = properties[ii];
+		GBindingTransformFunc transform_to = NULL;
+		GBindingTransformFunc transform_from = NULL;
+		ESourceExtension *extension;
+		const gchar *source_property;
+		const gchar *target_property;
+		gint binding_index;
+
+		binding_index = subclass_get_binding_index (pspec);
+
+		/* Bind the CamelSettings property to
+		 * one in a different ESourceExtension. */
+		if (binding_index >= 0) {
+			BindingData *binding;
+
+			binding = &bindings[binding_index];
+
+			extension = e_source_get_extension (
+				source, binding->extension_name);
+
+			source_property = binding->extension_property_name;
+			target_property = binding->settings_property_name;
+
+			transform_to = binding->extension_to_settings;
+			transform_from = binding->settings_to_extension;
+
+		/* Bind the CamelSettings property to our own
+		 * equivalent E_SOURCE_PARAM_SETTING property. */
+		} else {
+			GValue *value;
+
+			extension = E_SOURCE_EXTENSION (object);
+
+			source_property = pspec->name;
+			target_property = pspec->name;
+
+			/* Initialize the array element to
+			 * hold the GParamSpec's value type. */
+			value = &g_array_index (
+				priv->value_array, GValue, array_index++);
+			g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+			/* Set the array element to the GParamSpec's default
+			 * value.  This allows us to avoid declaring our own
+			 * properties with a G_PARAM_CONSTRUCT flag. */
+			g_param_value_set_default (pspec, value);
+		}
+
+		g_object_bind_property_full (
+			extension, source_property,
+			priv->settings, target_property,
+			G_BINDING_BIDIRECTIONAL |
+			G_BINDING_SYNC_CREATE,
+			transform_to, transform_from,
+			NULL, (GDestroyNotify) NULL);
+	}
+
+	/* Trim off any extra array elements. */
+	g_array_set_size (priv->value_array, array_index);
+
+	g_free (properties);
+}
+
+static void
+e_source_camel_class_init (ESourceCamelClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESourceCamelPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->get_property = source_camel_get_property;
+	object_class->dispose = source_camel_dispose;
+	object_class->finalize = source_camel_finalize;
+	object_class->constructed = source_camel_constructed;
+
+	/* CamelSettings itself has no properties. */
+	class->settings_type = CAMEL_TYPE_SETTINGS;
+
+	/* XXX This kind of stomps on CamelSettings' namespace, but it's
+	 *     unlikely a CamelSettings subclass would define a property
+	 *     named "settings". */
+	g_object_class_install_property (
+		object_class,
+		PROP_SETTINGS,
+		g_param_spec_object (
+			"settings",
+			"Settings",
+			"The CamelSettings instance being proxied",
+			CAMEL_TYPE_SETTINGS,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_source_camel_init (ESourceCamel *extension)
+{
+	GArray *value_array;
+
+	/* Zero-fill array elements when they are allocated. */
+	value_array = g_array_new (FALSE, TRUE, sizeof (GValue));
+
+	extension->priv = E_SOURCE_CAMEL_GET_PRIVATE (extension);
+	extension->priv->value_array = value_array;
+}
+
+/**
+ * e_source_camel_register_types:
+ *
+ * Creates and registers subclasses of #ESourceCamel for each available
+ * #CamelProvider.  This function should be called once during application
+ * or library initialization.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_camel_register_types (void)
+{
+	GList *list, *link;
+
+	/* This implicitly takes care of provider initialization. */
+	list = camel_provider_list (TRUE);
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		CamelProvider *provider;
+		gint ii;
+
+		provider = (CamelProvider *) link->data;
+
+		/* This is the novel part: generate and register
+		 * a new ESourceCamel subclass on-the-fly for each
+		 * object type listed in the provider. */
+		for (ii = 0; ii < CAMEL_NUM_PROVIDER_TYPES; ii++) {
+			CamelServiceClass *service_class = NULL;
+			GType service_type;
+
+			service_type = provider->object_types[ii];
+
+			if (g_type_is_a (service_type, CAMEL_TYPE_SERVICE))
+				service_class = g_type_class_ref (service_type);
+
+			if (service_class != NULL) {
+				e_source_camel_generate_subtype (
+					provider->protocol,
+					service_class->settings_type);
+				g_type_class_unref (service_class);
+			}
+		}
+	}
+
+	g_list_free (list);
+}
+
+/**
+ * e_source_camel_generate_subtype:
+ * @protocol: a #CamelProvider protocol
+ * @settings_type: a subtype of #CamelSettings
+ *
+ * Generates a custom #ESourceCamel subtype for @protocol.  Instances of the
+ * new subtype will contain a #CamelSettings instance of type @settings_type.
+ *
+ * This function is called as part of e_source_camel_register_types() and
+ * should not be called explicitly, except by some groupware packages that
+ * need to share package-specific settings across their mail, calendar and
+ * address book components.  In that case the groupware package may choose
+ * to subclass #CamelSettings rather than #ESourceExtension since libcamel
+ * is the lowest common denominator across all components.  This function
+ * provides a way for the calendar and address book components of such a
+ * package to generate an #ESourceCamel subtype for its #CamelSettings
+ * subtype without having to load all available #CamelProvider modules.
+ *
+ * Returns: the #GType of the generated #ESourceCamel subtype
+ *
+ * Since: 3.6
+ **/
+GType
+e_source_camel_generate_subtype (const gchar *protocol,
+                                 GType settings_type)
+{
+	ESourceCamelClass *class;
+	GObjectClass *settings_class;
+	GParamSpec **properties;
+	guint ii, n_properties;
+	guint prop_id = 1;
+	GTypeInfo type_info;
+	GType parent_type;
+	GType type;
+	const gchar *type_name;
+	const gchar *extension_name;
+
+	g_return_val_if_fail (protocol != NULL, G_TYPE_INVALID);
+
+	type_name = e_source_camel_get_type_name (protocol);
+	extension_name = e_source_camel_get_extension_name (protocol);
+
+	/* Check if the type name is already registered. */
+	type = g_type_from_name (type_name);
+	if (type != G_TYPE_INVALID)
+		return type;
+
+	/* The settings type must be derived from CAMEL_TYPE_SETTINGS. */
+	if (!g_type_is_a (settings_type, CAMEL_TYPE_SETTINGS)) {
+		g_warning (
+			"%s: Invalid settings type '%s' for protocol '%s'",
+			G_STRFUNC, g_type_name (settings_type), protocol);
+		return G_TYPE_INVALID;
+	}
+
+	memset (&type_info, 0, sizeof (GTypeInfo));
+	type_info.class_size = sizeof (ESourceCamelClass);
+	type_info.class_init = subclass_class_init;
+	type_info.instance_size = sizeof (ESourceCamel);
+	type_info.instance_init = subclass_instance_init;
+
+	parent_type = E_TYPE_SOURCE_CAMEL;
+	type = g_type_register_static (parent_type, type_name, &type_info, 0);
+
+	/* Since we have first access to the newly registered GType, and
+	 * because initializing its class structure requires some of the
+	 * arguments we were passed, we'll complete class initialization
+	 * here rather than trying to do it all in subclass_init(). */
+
+	class = g_type_class_ref (type);
+	settings_class = g_type_class_ref (settings_type);
+
+	/* Initialize more class members. */
+	class->settings_type = G_OBJECT_CLASS_TYPE (settings_class);
+	class->parent_class.name = g_intern_string (extension_name);
+
+	/* For each property in the CamelSettings class, register
+	 * an equivalent GObject property in this class and add an
+	 * E_SOURCE_PARAM_SETTING flag so the value gets written to
+	 * the ESource's key file. */
+
+	properties = g_object_class_list_properties (
+		settings_class, &n_properties);
+
+	for (ii = 0; ii < n_properties; ii++) {
+		GParamSpec *pspec;
+
+		/* Some properties in CamelSettings may be covered
+		 * by other ESourceExtensions.  Skip them here. */
+		if (subclass_get_binding_index (properties[ii]) >= 0)
+			continue;
+
+		pspec = param_spec_clone (properties[ii]);
+		pspec->flags |= E_SOURCE_PARAM_SETTING;
+
+		/* Clear the G_PARAM_CONSTRUCT flag.  We apply default
+		 * property values to our GValue array during instance
+		 * initialization. */
+		pspec->flags &= ~G_PARAM_CONSTRUCT;
+
+		g_object_class_install_property (
+			G_OBJECT_CLASS (class), prop_id++, pspec);
+	}
+
+	g_free (properties);
+
+	g_type_class_unref (class);
+	g_type_class_unref (settings_class);
+
+	return type;
+}
+
+/**
+ * e_source_camel_get_settings:
+ * @extension: an #ESourceCamel
+ *
+ * Returns @extension's #ESourceCamel:settings instance, pre-configured
+ * from the #ESource to which @extension belongs.  Changes to the #ESource
+ * will automatically propagate to the #ESourceCamel:settings instance and
+ * vice versa.
+ *
+ * This is essentially the glue that binds #ESource to #CamelService.
+ * See e_source_camel_configure_service().
+ *
+ * Returns: a configured #CamelSettings instance
+ *
+ * Since: 3.6
+ **/
+CamelSettings *
+e_source_camel_get_settings (ESourceCamel *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_CAMEL (extension), NULL);
+
+	return extension->priv->settings;
+}
+
+/**
+ * e_source_camel_get_type_name:
+ * @protocol: a #CamelProvider protocol
+ *
+ * Returns the #GType name of the registered #ESourceCamel subtype for
+ * @protocol.
+ *
+ * For example, given a protocol named "imap" the function would return
+ * "ESourceCamelImap".
+ *
+ * Returns: the #ESourceCamel type name for @protocol
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_camel_get_type_name (const gchar *protocol)
+{
+	gchar *buffer;
+
+	g_return_val_if_fail (protocol != NULL, NULL);
+
+	buffer = g_alloca (strlen (protocol) + 16);
+	g_sprintf (buffer, "ESourceCamel%s", protocol);
+	buffer[12] = g_ascii_toupper (buffer[12]);
+
+	return g_intern_string (buffer);
+}
+
+/**
+ * e_source_camel_get_extension_name:
+ * @protocol: a #CamelProvider protocol
+ *
+ * Returns the extension name for the #ESourceCamel subtype for @protocol.
+ * The extension name can then be passed to e_source_get_extension() to
+ * obtain an instance of the #ESourceCamel subtype.
+ *
+ * For example, given a protocol named "imap" the function would return
+ * "Imap Backend".
+ *
+ * Returns: the #ESourceCamel extension name for @protocol
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_camel_get_extension_name (const gchar *protocol)
+{
+	gchar *buffer;
+
+	g_return_val_if_fail (protocol != NULL, NULL);
+
+	/* Use the term "backend" for consistency with other
+	 * calendar and address book backend extension names. */
+	buffer = g_alloca (strlen (protocol) + 16);
+	g_sprintf (buffer, "%s Backend", protocol);
+	buffer[0] = g_ascii_toupper (buffer[0]);
+
+	return g_intern_string (buffer);
+}
+
+/**
+ * e_source_camel_configure_service:
+ * @source: an #ESource
+ * @service: a #CamelService
+ *
+ * This function essentially glues together @source and @serivce so their
+ * configuration settings stay synchronized.  The glue itself is a shared
+ * #CamelSettings instance.
+ *
+ * Call this function immediately after creating a new #CamelService with
+ * camel_session_add_service().
+ *
+ * Since: 3.6
+ **/
+void
+e_source_camel_configure_service (ESource *source,
+                                  CamelService *service)
+{
+	ESourceCamel *extension;
+	CamelProvider *provider;
+	CamelSettings *settings;
+	const gchar *extension_name;
+
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+	provider = camel_service_get_provider (service);
+	g_return_if_fail (provider != NULL);
+
+	extension_name =
+		e_source_camel_get_extension_name (provider->protocol);
+	extension = e_source_get_extension (source, extension_name);
+
+	settings = e_source_camel_get_settings (extension);
+	camel_service_set_settings (service, settings);
+}
+
diff --git a/libedataserver/e-source-camel.h b/libedataserver/e-source-camel.h
new file mode 100644
index 0000000..703955e
--- /dev/null
+++ b/libedataserver/e-source-camel.h
@@ -0,0 +1,84 @@
+/*
+ * e-source-camel.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_SOURCE_CAMEL_H
+#define E_SOURCE_CAMEL_H
+
+#include <camel/camel.h>
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_CAMEL \
+	(e_source_camel_get_type ())
+#define E_SOURCE_CAMEL(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_CAMEL, ESourceCamel))
+#define E_SOURCE_CAMEL_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_CAMEL, ESourceCamelClass))
+#define E_IS_SOURCE_CAMEL(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_CAMEL))
+#define E_IS_SOURCE_CAMEL_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_CAMEL))
+#define E_SOURCE_CAMEL_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_CAMEL, ESourceCamelClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCamel ESourceCamel;
+typedef struct _ESourceCamelClass ESourceCamelClass;
+typedef struct _ESourceCamelPrivate ESourceCamelPrivate;
+
+/**
+ * ESourceCamel:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceCamel {
+	ESourceExtension parent;
+	ESourceCamelPrivate *priv;
+};
+
+struct _ESourceCamelClass {
+	ESourceExtensionClass parent_class;
+
+	/* Same idea as in CamelServiceClass. */
+	GType settings_type;
+};
+
+GType		e_source_camel_get_type		(void) G_GNUC_CONST;
+void		e_source_camel_register_types	(void);
+GType		e_source_camel_generate_subtype	(const gchar *protocol,
+						 GType settings_type);
+CamelSettings *	e_source_camel_get_settings	(ESourceCamel *extension);
+const gchar *	e_source_camel_get_type_name	(const gchar *protocol);
+const gchar *	e_source_camel_get_extension_name
+						(const gchar *protocol);
+void		e_source_camel_configure_service
+						(ESource *source,
+						 CamelService *service);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_CAMEL_H */
diff --git a/libedataserver/e-source-collection.c b/libedataserver/e-source-collection.c
new file mode 100644
index 0000000..7980878
--- /dev/null
+++ b/libedataserver/e-source-collection.c
@@ -0,0 +1,369 @@
+/*
+ * e-source-collection.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-collection
+ * @include: libedataserver/e-source-collection.h
+ * @short_description: #ESource extension for grouping related resources
+ *
+ * FIXME Add a longer description.
+ **/
+
+#include "e-source-collection.h"
+
+#define E_SOURCE_COLLECTION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_COLLECTION, ESourceCollectionPrivate))
+
+struct _ESourceCollectionPrivate {
+	GMutex *property_lock;
+	gchar *identity;
+	gboolean calendar_enabled;
+	gboolean contacts_enabled;
+	gboolean mail_enabled;
+};
+
+enum {
+	PROP_0,
+	PROP_CALENDAR_ENABLED,
+	PROP_CONTACTS_ENABLED,
+	PROP_IDENTITY,
+	PROP_MAIL_ENABLED
+};
+
+G_DEFINE_TYPE (
+	ESourceCollection,
+	e_source_collection,
+	E_TYPE_SOURCE_BACKEND)
+
+static void
+source_collection_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CALENDAR_ENABLED:
+			e_source_collection_set_calendar_enabled (
+				E_SOURCE_COLLECTION (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_CONTACTS_ENABLED:
+			e_source_collection_set_contacts_enabled (
+				E_SOURCE_COLLECTION (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_IDENTITY:
+			e_source_collection_set_identity (
+				E_SOURCE_COLLECTION (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_MAIL_ENABLED:
+			e_source_collection_set_mail_enabled (
+				E_SOURCE_COLLECTION (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_collection_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CALENDAR_ENABLED:
+			g_value_set_boolean (
+				value,
+				e_source_collection_get_calendar_enabled (
+				E_SOURCE_COLLECTION (object)));
+			return;
+
+		case PROP_CONTACTS_ENABLED:
+			g_value_set_boolean (
+				value,
+				e_source_collection_get_contacts_enabled (
+				E_SOURCE_COLLECTION (object)));
+			return;
+
+		case PROP_IDENTITY:
+			g_value_take_string (
+				value,
+				e_source_collection_dup_identity (
+				E_SOURCE_COLLECTION (object)));
+			return;
+
+		case PROP_MAIL_ENABLED:
+			g_value_set_boolean (
+				value,
+				e_source_collection_get_mail_enabled (
+				E_SOURCE_COLLECTION (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_collection_finalize (GObject *object)
+{
+	ESourceCollectionPrivate *priv;
+
+	priv = E_SOURCE_COLLECTION_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->identity);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_collection_parent_class)->finalize (object);
+}
+
+static void
+e_source_collection_class_init (ESourceCollectionClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceCollectionPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_collection_set_property;
+	object_class->get_property = source_collection_get_property;
+	object_class->finalize = source_collection_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_COLLECTION;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CALENDAR_ENABLED,
+		g_param_spec_boolean (
+			"calendar-enabled",
+			"Calendar Enabled",
+			"Whether calendar resources are enabled",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CONTACTS_ENABLED,
+		g_param_spec_boolean (
+			"contacts-enabled",
+			"Contacts Enabled",
+			"Whether contact resources are enabled",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_IDENTITY,
+		g_param_spec_string (
+			"identity",
+			"Identity",
+			"Uniquely identifies the account "
+			"at the service provider",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_MAIL_ENABLED,
+		g_param_spec_boolean (
+			"mail-enabled",
+			"Mail Enabled",
+			"Whether mail resources are enabled",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_collection_init (ESourceCollection *extension)
+{
+	extension->priv = E_SOURCE_COLLECTION_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_collection_get_identity:
+ * @extension: an #ESourceCollection
+ *
+ * Returns the string used to uniquely identify the user account at
+ * the service provider.  Often this is an email address or user name.
+ *
+ * Return: the collection identity
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_collection_get_identity (ESourceCollection *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), NULL);
+
+	return extension->priv->identity;
+}
+
+/**
+ * e_source_collection_dup_identity:
+ * @extension: an #ESourceCollection
+ *
+ * Thread-safe variation of e_source_collection_get_identity().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceCollection:identity
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_collection_dup_identity (ESourceCollection *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_collection_get_identity (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_collection_set_identity:
+ * @extension: an #ESourceCollection
+ * @identity: the collection identity
+ *
+ * Sets the string used to uniquely identify the user account at the
+ * service provider.  Often this is an email address or user name.
+ *
+ * The internal copy of @identity is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is
+ * set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_collection_set_identity (ESourceCollection *extension,
+                                  const gchar *identity)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_COLLECTION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (identity);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->identity);
+	extension->priv->identity = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "identity");
+}
+
+gboolean
+e_source_collection_get_calendar_enabled (ESourceCollection *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), FALSE);
+
+	return extension->priv->calendar_enabled;
+}
+
+void
+e_source_collection_set_calendar_enabled (ESourceCollection *extension,
+                                          gboolean calendar_enabled)
+{
+	g_return_if_fail (E_IS_SOURCE_COLLECTION (extension));
+
+	extension->priv->calendar_enabled = calendar_enabled;
+
+	g_object_notify (G_OBJECT (extension), "calendar-enabled");
+}
+
+gboolean
+e_source_collection_get_contacts_enabled (ESourceCollection *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), FALSE);
+
+	return extension->priv->contacts_enabled;
+}
+
+void
+e_source_collection_set_contacts_enabled (ESourceCollection *extension,
+                                          gboolean contacts_enabled)
+{
+	g_return_if_fail (E_IS_SOURCE_COLLECTION (extension));
+
+	extension->priv->contacts_enabled = contacts_enabled;
+
+	g_object_notify (G_OBJECT (extension), "contacts-enabled");
+}
+
+gboolean
+e_source_collection_get_mail_enabled (ESourceCollection *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), FALSE);
+
+	return extension->priv->mail_enabled;
+}
+
+void
+e_source_collection_set_mail_enabled (ESourceCollection *extension,
+                                      gboolean mail_enabled)
+{
+	g_return_if_fail (E_IS_SOURCE_COLLECTION (extension));
+
+	extension->priv->mail_enabled = mail_enabled;
+
+	g_object_notify (G_OBJECT (extension), "mail-enabled");
+}
+
diff --git a/libedataserver/e-source-collection.h b/libedataserver/e-source-collection.h
new file mode 100644
index 0000000..b7e95be
--- /dev/null
+++ b/libedataserver/e-source-collection.h
@@ -0,0 +1,103 @@
+/*
+ * e-source-collection.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_SOURCE_COLLECTION_H
+#define E_SOURCE_COLLECTION_H
+
+#include <libedataserver/e-source-backend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_COLLECTION \
+	(e_source_collection_get_type ())
+#define E_SOURCE_COLLECTION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_COLLECTION, ESourceCollection))
+#define E_SOURCE_COLLECTION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_COLLECTION, ESourceCollectionClass))
+#define E_IS_SOURCE_COLLECTION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_COLLECTION))
+#define E_IS_SOURCE_COLLECTION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_COLLECTION))
+#define E_SOURCE_COLLECTION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_COLLECTION, ESourceCollectionClass))
+
+/**
+ * E_SOURCE_EXTENSION_COLLECTION:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceCollection.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_COLLECTION "Collection"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCollection ESourceCollection;
+typedef struct _ESourceCollectionClass ESourceCollectionClass;
+typedef struct _ESourceCollectionPrivate ESourceCollectionPrivate;
+
+/**
+ * ESourceCollection:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceCollection {
+	ESourceBackend parent;
+	ESourceCollectionPrivate *priv;
+};
+
+struct _ESourceCollectionClass {
+	ESourceBackendClass parent_class;
+};
+
+GType		e_source_collection_get_type	(void) G_GNUC_CONST;
+const gchar *	e_source_collection_get_identity
+						(ESourceCollection *extension);
+gchar *		e_source_collection_dup_identity
+						(ESourceCollection *extension);
+void		e_source_collection_set_identity
+						(ESourceCollection *extension,
+						 const gchar *identity);
+gboolean	e_source_collection_get_calendar_enabled
+						(ESourceCollection *extension);
+void		e_source_collection_set_calendar_enabled
+						(ESourceCollection *extension,
+						 gboolean calendar_enabled);
+gboolean	e_source_collection_get_contacts_enabled
+						(ESourceCollection *extension);
+void		e_source_collection_set_contacts_enabled
+						(ESourceCollection *extension,
+						 gboolean contacts_enabled);
+gboolean	e_source_collection_get_mail_enabled
+						(ESourceCollection *extension);
+void		e_source_collection_set_mail_enabled
+						(ESourceCollection *extension,
+						 gboolean mail_enabled);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_COLLECTION_H */
+
diff --git a/libedataserver/e-source-enums.h b/libedataserver/e-source-enums.h
new file mode 100644
index 0000000..d4c7214
--- /dev/null
+++ b/libedataserver/e-source-enums.h
@@ -0,0 +1,62 @@
+/*
+ * e-source-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_SOURCE_ENUMS_H
+#define E_SOURCE_ENUMS_H
+
+/**
+ * EMdnResponsePolicy:
+ * @E_MDN_RESPONSE_POLICY_NEVER:
+ *   Never respond to an MDN request.
+ * @E_MDN_RESPONSE_POLICY_ALWAYS:
+ *   Always respond to an MDN request.
+ * @E_MDN_RESPONSE_POLICY_ASK:
+ *   Ask the user before responding to an MDN request.
+ *
+ * Policy for responding to Message Disposition Notification requests
+ * (i.e. a Disposition-Notification-To header) when receiving messages.
+ * See RFC 2298 for more information about MDN requests.
+ *
+ * Since: 3.6
+ **/
+typedef enum {
+	E_MDN_RESPONSE_POLICY_NEVER,
+	E_MDN_RESPONSE_POLICY_ALWAYS,
+	E_MDN_RESPONSE_POLICY_ASK
+} EMdnResponsePolicy;
+
+/**
+ * ESourceAuthenticationResult:
+ * @E_SOURCE_AUTHENTICATION_ERROR:
+ *   An error occurred while authenticating.
+ * @E_SOURCE_AUTHENTICATION_ACCEPTED:
+ *   Server requesting authentication accepted password.
+ * @E_SOURCE_AUTHENTICATION_REJECTED:
+ *   Server requesting authentication rejected password.
+ *
+ * Status codes used by the #ESourceAuthenticator interface.
+ *
+ * Since: 3.6
+ **/
+typedef enum {
+	E_SOURCE_AUTHENTICATION_ERROR,
+	E_SOURCE_AUTHENTICATION_ACCEPTED,
+	E_SOURCE_AUTHENTICATION_REJECTED
+} ESourceAuthenticationResult;
+
+#endif /* E_SOURCE_ENUMS_H */
diff --git a/libedataserver/e-source-extension.c b/libedataserver/e-source-extension.c
new file mode 100644
index 0000000..07024cf
--- /dev/null
+++ b/libedataserver/e-source-extension.c
@@ -0,0 +1,190 @@
+/*
+ * e-source-extension.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-extension
+ * @include: libedataserver/e-source-extension.h
+ * @short_description: Base class for #ESource extensions
+ *
+ * #ESourceExtension is an abstract base class for #ESource extension
+ * objects.  An #ESourceExtension object basically just maps the keys in
+ * a key file group to a set of #GObject properties.  The name of the key
+ * file group doubles as the name of the #ESourceExtension object.
+ *
+ * #ESourceExtension objects are accessed through e_source_get_extension().
+ **/
+
+#include "e-source-extension.h"
+
+#define E_SOURCE_EXTENSION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_EXTENSION, ESourceExtensionPrivate))
+
+struct _ESourceExtensionPrivate {
+	gpointer source;  /* weak pointer */
+};
+
+enum {
+	PROP_0,
+	PROP_SOURCE
+};
+
+G_DEFINE_ABSTRACT_TYPE (
+	ESourceExtension,
+	e_source_extension,
+	G_TYPE_OBJECT)
+
+static void
+source_extension_set_source (ESourceExtension *extension,
+                             ESource *source)
+{
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (extension->priv->source == NULL);
+
+	extension->priv->source = source;
+
+	g_object_add_weak_pointer (
+		G_OBJECT (source), &extension->priv->source);
+}
+
+static void
+source_extension_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SOURCE:
+			source_extension_set_source (
+				E_SOURCE_EXTENSION (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_extension_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SOURCE:
+			g_value_set_object (
+				value, e_source_extension_get_source (
+				E_SOURCE_EXTENSION (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_extension_dispose (GObject *object)
+{
+	ESourceExtensionPrivate *priv;
+
+	priv = E_SOURCE_EXTENSION_GET_PRIVATE (object);
+
+	if (priv->source != NULL) {
+		g_object_remove_weak_pointer (
+			G_OBJECT (priv->source), &priv->source);
+		priv->source = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_source_extension_parent_class)->dispose (object);
+}
+
+static void
+source_extension_notify (GObject *object,
+                         GParamSpec *pspec)
+{
+	ESource *source;
+	ESourceExtension *extension;
+
+	extension = E_SOURCE_EXTENSION (object);
+	source = e_source_extension_get_source (extension);
+	g_return_if_fail (source != NULL);
+
+	if ((pspec->flags & E_SOURCE_PARAM_SETTING) != 0)
+		e_source_changed (source);
+}
+
+static void
+source_extension_constructed (GObject *object)
+{
+	/* This allows subclasses to chain up safely since GObject
+	 * does not implement this method, and we might want to do
+	 * something here in the future. */
+}
+
+static void
+e_source_extension_class_init (ESourceExtensionClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESourceExtensionPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_extension_set_property;
+	object_class->get_property = source_extension_get_property;
+	object_class->dispose = source_extension_dispose;
+	object_class->notify = source_extension_notify;
+	object_class->constructed = source_extension_constructed;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SOURCE,
+		g_param_spec_object (
+			"source",
+			"Source",
+			"The ESource being extended",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_source_extension_init (ESourceExtension *extension)
+{
+	extension->priv = E_SOURCE_EXTENSION_GET_PRIVATE (extension);
+}
+
+/**
+ * e_source_extension_get_source:
+ * @extension: an #ESourceExtension
+ *
+ * Returns the #ESource instance to which @extension belongs.
+ *
+ * Returns: the #ESource instance
+ **/
+ESource *
+e_source_extension_get_source (ESourceExtension *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_EXTENSION (extension), NULL);
+
+	/* If the ESource was finalized and our weak pointer set this
+	 * to NULL, then the type cast macro will fail and we'll get a
+	 * runtime warning about it, which is what we want. */
+	return E_SOURCE (extension->priv->source);
+}
+
diff --git a/libedataserver/e-source-extension.h b/libedataserver/e-source-extension.h
new file mode 100644
index 0000000..4095696
--- /dev/null
+++ b/libedataserver/e-source-extension.h
@@ -0,0 +1,73 @@
+/*
+ * e-source-extension.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_SOURCE_EXTENSION_H
+#define E_SOURCE_EXTENSION_H
+
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_EXTENSION \
+	(e_source_extension_get_type ())
+#define E_SOURCE_EXTENSION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_EXTENSION, ESourceExtension))
+#define E_SOURCE_EXTENSION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_EXTENSION, ESourceExtensionClass))
+#define E_IS_SOURCE_EXTENSION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_EXTENSION))
+#define E_IS_SOURCE_EXTENSION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_EXTENSION))
+#define E_SOURCE_EXTENSION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_EXTENSION, ESourceExtensionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceExtension ESourceExtension;
+typedef struct _ESourceExtensionClass ESourceExtensionClass;
+typedef struct _ESourceExtensionPrivate ESourceExtensionPrivate;
+
+/**
+ * ESourceExtension:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceExtension {
+	GObject parent;
+	ESourceExtensionPrivate *priv;
+};
+
+struct _ESourceExtensionClass {
+	GObjectClass parent_class;
+
+	const gchar *name;
+};
+
+GType		e_source_extension_get_type	(void) G_GNUC_CONST;
+ESource *	e_source_extension_get_source	(ESourceExtension *extension);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_EXTENSION_H */
diff --git a/libedataserver/e-source-goa.c b/libedataserver/e-source-goa.c
new file mode 100644
index 0000000..0555dd9
--- /dev/null
+++ b/libedataserver/e-source-goa.c
@@ -0,0 +1,239 @@
+/*
+ * e-source-goa.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-goa
+ * @include: libedataserver/e-source-goa.h
+ * @short_description: #ESource extension for GNOME Online Accounts
+ *
+ * The #ESourceGoa extension associates an #ESource with a #GoaAccount.
+ * This extension is usually found in a top-level #ESource, with various
+ * mail, calendar and address book data sources as children.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-goa.h>
+ *
+ *   ESourceGoa *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
+ * ]|
+ **/
+
+#include "e-source-goa.h"
+
+#define E_SOURCE_GOA_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_GOA, ESourceGoaPrivate))
+
+struct _ESourceGoaPrivate {
+	GMutex *property_lock;
+	gchar *account_id;
+};
+
+enum {
+	PROP_0,
+	PROP_ACCOUNT_ID
+};
+
+G_DEFINE_TYPE (
+	ESourceGoa,
+	e_source_goa,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_goa_set_property (GObject *object,
+                         guint property_id,
+                         const GValue *value,
+                         GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ACCOUNT_ID:
+			e_source_goa_set_account_id (
+				E_SOURCE_GOA (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_goa_get_property (GObject *object,
+                         guint property_id,
+                         GValue *value,
+                         GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ACCOUNT_ID:
+			g_value_take_string (
+				value,
+				e_source_goa_dup_account_id (
+				E_SOURCE_GOA (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_goa_finalize (GObject *object)
+{
+	ESourceGoaPrivate *priv;
+
+	priv = E_SOURCE_GOA_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->account_id);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_goa_parent_class)->finalize (object);
+}
+
+static void
+e_source_goa_class_init (ESourceGoaClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceGoaPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_goa_set_property;
+	object_class->get_property = source_goa_get_property;
+	object_class->finalize = source_goa_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_GOA;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ACCOUNT_ID,
+		g_param_spec_string (
+			"account-id",
+			"Account ID",
+			"GNOME Online Account ID",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_goa_init (ESourceGoa *extension)
+{
+	extension->priv = E_SOURCE_GOA_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_goa_get_account_id:
+ * @extension: an #ESourceGoa
+ *
+ * Returns the identifier string of the GNOME Online Account associated
+ * with the #ESource to which @extension belongs.
+ *
+ * Returns: the associated GNOME Online Account ID
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_goa_get_account_id (ESourceGoa *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL);
+
+	return extension->priv->account_id;
+}
+
+/**
+ * e_source_goa_dup_account_id:
+ * @extension: an #ESourceGoa
+ *
+ * Thread-safe variation of e_source_goa_get_account_id().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceGoa:account-id
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_goa_dup_account_id (ESourceGoa *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_goa_get_account_id (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_goa_set_account_id:
+ * @extension: an #ESourceGoa
+ * @account_id: the associated GNOME Online Account ID, or %NULL
+ *
+ * Sets the identifier string of the GNOME Online Account associated
+ * with the #ESource to which @extension belongs.
+ *
+ * The internal copy of @account_id is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_goa_set_account_id (ESourceGoa *extension,
+                             const gchar *account_id)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_GOA (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (account_id);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->account_id);
+	extension->priv->account_id = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "account-id");
+}
+
diff --git a/libedataserver/e-source-goa.h b/libedataserver/e-source-goa.h
new file mode 100644
index 0000000..ed68d9f
--- /dev/null
+++ b/libedataserver/e-source-goa.h
@@ -0,0 +1,85 @@
+/*
+ * e-source-goa.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_SOURCE_GOA_H
+#define E_SOURCE_GOA_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_GOA \
+	(e_source_goa_get_type ())
+#define E_SOURCE_GOA(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_GOA, ESourceGoa))
+#define E_SOURCE_GOA_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_GOA, ESourceGoaClass))
+#define E_IS_SOURCE_GOA(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_GOA))
+#define E_IS_SOURCE_GOA_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_GOA))
+#define E_SOURCE_GOA_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_GOA, ESourceGoaClass))
+
+/**
+ * E_SOURCE_EXTENSION_GOA:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceGoa.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_GOA "GNOME Online Accounts"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceGoa ESourceGoa;
+typedef struct _ESourceGoaClass ESourceGoaClass;
+typedef struct _ESourceGoaPrivate ESourceGoaPrivate;
+
+/**
+ * ESourceGoa:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceGoa {
+	ESourceExtension parent;
+	ESourceGoaPrivate *priv;
+};
+
+struct _ESourceGoaClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_goa_get_type		(void) G_GNUC_CONST;
+const gchar *	e_source_goa_get_account_id	(ESourceGoa *extension);
+gchar *		e_source_goa_dup_account_id	(ESourceGoa *extension);
+void		e_source_goa_set_account_id	(ESourceGoa *extension,
+						 const gchar *account_id);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_GOA_H */
+
diff --git a/libedataserver/e-source-mail-account.c b/libedataserver/e-source-mail-account.c
new file mode 100644
index 0000000..547f848
--- /dev/null
+++ b/libedataserver/e-source-mail-account.c
@@ -0,0 +1,225 @@
+/*
+ * e-source-mail-account.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-mail-account
+ * @include: libedataserver/e-source-mail-account.h
+ * @short_description: #ESource extension for an email account
+ *
+ * The #ESourceMailAccount extension identifies the #ESource as a
+ * mail account and also links to a default "mail identity" to use.
+ * See #ESourceMailIdentity for more information about identities.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-mail-account.h>
+ *
+ *   ESourceMailAccount *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+ * ]|
+ **/
+
+#include "e-source-mail-account.h"
+
+#include <libedataserver/e-source-enumtypes.h>
+#include <libedataserver/e-source-mail-identity.h>
+
+#define E_SOURCE_MAIL_ACCOUNT_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccountPrivate))
+
+struct _ESourceMailAccountPrivate {
+	GMutex *property_lock;
+	gchar *identity_uid;
+};
+
+enum {
+	PROP_0,
+	PROP_IDENTITY_UID
+};
+
+G_DEFINE_TYPE (
+	ESourceMailAccount,
+	e_source_mail_account,
+	E_TYPE_SOURCE_BACKEND)
+
+static void
+source_mail_account_set_property (GObject *object,
+                                  guint property_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_IDENTITY_UID:
+			e_source_mail_account_set_identity_uid (
+				E_SOURCE_MAIL_ACCOUNT (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_account_get_property (GObject *object,
+                                  guint property_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_IDENTITY_UID:
+			g_value_take_string (
+				value,
+				e_source_mail_account_dup_identity_uid (
+				E_SOURCE_MAIL_ACCOUNT (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_account_finalize (GObject *object)
+{
+	ESourceMailAccountPrivate *priv;
+
+	priv = E_SOURCE_MAIL_ACCOUNT_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->identity_uid);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_mail_account_parent_class)->finalize (object);
+}
+
+static void
+e_source_mail_account_class_init (ESourceMailAccountClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceMailAccountPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_mail_account_set_property;
+	object_class->get_property = source_mail_account_get_property;
+	object_class->finalize = source_mail_account_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_IDENTITY_UID,
+		g_param_spec_string (
+			"identity-uid",
+			"Identity UID",
+			"ESource UID of a Mail Identity",
+			"self",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_mail_account_init (ESourceMailAccount *extension)
+{
+	extension->priv = E_SOURCE_MAIL_ACCOUNT_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_mail_account_get_identity_uid:
+ * @extension: an #ESourceMailAccount
+ *
+ * Returns the #ESource:uid of the #ESource that describes the mail
+ * identity to be used for this account.
+ *
+ * Returns: the mail identity #ESource:uid
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_account_get_identity_uid (ESourceMailAccount *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension), NULL);
+
+	return extension->priv->identity_uid;
+}
+
+/**
+ * e_source_mail_account_dup_identity_uid:
+ * @extension: an #ESourceMailAccount
+ *
+ * Thread-safe variation of e_source_mail_account_get_identity_uid().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailAccount:identity-uid
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_account_dup_identity_uid (ESourceMailAccount *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_account_get_identity_uid (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_account_set_identity_uid:
+ * @extension: an #ESourceMailAccount
+ * @identity_uid: the mail identity #ESource:uid, or %NULL
+ *
+ * Sets the #ESource:uid of the #ESource that describes the mail
+ * identity to be used for this account.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_account_set_identity_uid (ESourceMailAccount *extension,
+                                        const gchar *identity_uid)
+{
+	g_return_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_free (extension->priv->identity_uid);
+	extension->priv->identity_uid = g_strdup (identity_uid);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "identity-uid");
+}
+
diff --git a/libedataserver/e-source-mail-account.h b/libedataserver/e-source-mail-account.h
new file mode 100644
index 0000000..c956ae3
--- /dev/null
+++ b/libedataserver/e-source-mail-account.h
@@ -0,0 +1,88 @@
+/*
+ * e-source-mail-account.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_SOURCE_MAIL_ACCOUNT_H
+#define E_SOURCE_MAIL_ACCOUNT_H
+
+#include <libedataserver/e-source-backend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MAIL_ACCOUNT \
+	(e_source_mail_account_get_type ())
+#define E_SOURCE_MAIL_ACCOUNT(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccount))
+#define E_SOURCE_MAIL_ACCOUNT_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccountClass))
+#define E_IS_SOURCE_MAIL_ACCOUNT(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MAIL_ACCOUNT))
+#define E_IS_SOURCE_MAIL_ACCOUNT_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MAIL_ACCOUNT))
+#define E_SOURCE_MAIL_ACCOUNT_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccountClass))
+
+/**
+ * E_SOURCE_EXTENSION_MAIL_ACCOUNT:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMailAccount.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MAIL_ACCOUNT "Mail Account"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceMailAccount ESourceMailAccount;
+typedef struct _ESourceMailAccountClass ESourceMailAccountClass;
+typedef struct _ESourceMailAccountPrivate ESourceMailAccountPrivate;
+
+/**
+ * ESourceMailAccount:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMailAccount {
+	ESourceBackend parent;
+	ESourceMailAccountPrivate *priv;
+};
+
+struct _ESourceMailAccountClass {
+	ESourceBackendClass parent_class;
+};
+
+GType		e_source_mail_account_get_type
+					(void) G_GNUC_CONST;
+const gchar *	e_source_mail_account_get_identity_uid
+					(ESourceMailAccount *extension);
+gchar *		e_source_mail_account_dup_identity_uid
+					(ESourceMailAccount *extension);
+void		e_source_mail_account_set_identity_uid
+					(ESourceMailAccount *extension,
+					 const gchar *identity_uid);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_MAIL_ACCOUNT_H */
diff --git a/libedataserver/e-source-mail-composition.c b/libedataserver/e-source-mail-composition.c
new file mode 100644
index 0000000..e1d2cc8
--- /dev/null
+++ b/libedataserver/e-source-mail-composition.c
@@ -0,0 +1,653 @@
+/*
+ * e-source-mail-composition.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-mail-composition
+ * @include: libedataserver/e-source-mail-composition.h
+ * @short_description: #ESource extension for mail composition settings
+ *
+ * The #ESourceMailComposition extension tracks settings to be applied
+ * when composing a new mail message.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-mail-composition.h>
+ *
+ *   ESourceMailComposition *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
+ * ]|
+ **/
+
+#include "e-source-mail-composition.h"
+
+#define E_SOURCE_MAIL_COMPOSITION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailCompositionPrivate))
+
+struct _ESourceMailCompositionPrivate {
+	GMutex *property_lock;
+	gchar **bcc;
+	gchar **cc;
+	gchar *drafts_folder;
+	gchar *templates_folder;
+	gboolean sign_imip;
+};
+
+enum {
+	PROP_0,
+	PROP_BCC,
+	PROP_CC,
+	PROP_DRAFTS_FOLDER,
+	PROP_SIGN_IMIP,
+	PROP_TEMPLATES_FOLDER
+};
+
+G_DEFINE_TYPE (
+	ESourceMailComposition,
+	e_source_mail_composition,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_mail_composition_set_property (GObject *object,
+                                      guint property_id,
+                                      const GValue *value,
+                                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_BCC:
+			e_source_mail_composition_set_bcc (
+				E_SOURCE_MAIL_COMPOSITION (object),
+				g_value_get_boxed (value));
+			return;
+
+		case PROP_CC:
+			e_source_mail_composition_set_cc (
+				E_SOURCE_MAIL_COMPOSITION (object),
+				g_value_get_boxed (value));
+			return;
+
+		case PROP_DRAFTS_FOLDER:
+			e_source_mail_composition_set_drafts_folder (
+				E_SOURCE_MAIL_COMPOSITION (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SIGN_IMIP:
+			e_source_mail_composition_set_sign_imip (
+				E_SOURCE_MAIL_COMPOSITION (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_TEMPLATES_FOLDER:
+			e_source_mail_composition_set_templates_folder (
+				E_SOURCE_MAIL_COMPOSITION (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_composition_get_property (GObject *object,
+                                      guint property_id,
+                                      GValue *value,
+                                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_BCC:
+			g_value_take_boxed (
+				value,
+				e_source_mail_composition_dup_bcc (
+				E_SOURCE_MAIL_COMPOSITION (object)));
+			return;
+
+		case PROP_CC:
+			g_value_take_boxed (
+				value,
+				e_source_mail_composition_dup_cc (
+				E_SOURCE_MAIL_COMPOSITION (object)));
+			return;
+
+		case PROP_DRAFTS_FOLDER:
+			g_value_take_string (
+				value,
+				e_source_mail_composition_dup_drafts_folder (
+				E_SOURCE_MAIL_COMPOSITION (object)));
+			return;
+
+		case PROP_SIGN_IMIP:
+			g_value_set_boolean (
+				value,
+				e_source_mail_composition_get_sign_imip (
+				E_SOURCE_MAIL_COMPOSITION (object)));
+			return;
+
+		case PROP_TEMPLATES_FOLDER:
+			g_value_take_string (
+				value,
+				e_source_mail_composition_dup_templates_folder (
+				E_SOURCE_MAIL_COMPOSITION (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_composition_finalize (GObject *object)
+{
+	ESourceMailCompositionPrivate *priv;
+
+	priv = E_SOURCE_MAIL_COMPOSITION_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_strfreev (priv->bcc);
+	g_strfreev (priv->cc);
+	g_free (priv->drafts_folder);
+	g_free (priv->templates_folder);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_mail_composition_parent_class)->
+		finalize (object);
+}
+
+static void
+e_source_mail_composition_class_init (ESourceMailCompositionClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (
+		class, sizeof (ESourceMailCompositionPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_mail_composition_set_property;
+	object_class->get_property = source_mail_composition_get_property;
+	object_class->finalize = source_mail_composition_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_BCC,
+		g_param_spec_boxed (
+			"bcc",
+			"Bcc",
+			"Recipients to blind carbon-copy",
+			G_TYPE_STRV,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CC,
+		g_param_spec_boxed (
+			"cc",
+			"Cc",
+			"Recipients to carbon-copy",
+			G_TYPE_STRV,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_DRAFTS_FOLDER,
+		g_param_spec_string (
+			"drafts-folder",
+			"Drafts Folder",
+			"Preferred folder for draft messages",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGN_IMIP,
+		g_param_spec_boolean (
+			"sign-imip",
+			"Sign iMIP",
+			"Include iMIP messages when signing",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_TEMPLATES_FOLDER,
+		g_param_spec_string (
+			"templates-folder",
+			"Templates Folder",
+			"Preferred folder for message templates",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_mail_composition_init (ESourceMailComposition *extension)
+{
+	extension->priv = E_SOURCE_MAIL_COMPOSITION_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_mail_composition_get_bcc:
+ * @extension: an #ESourceMailComposition
+ *
+ * Returns a %NULL-terminated string array of recipients which should
+ * automatically be added to the blind carbon-copy (Bcc) list when
+ * composing a new mail message.  The recipient strings should be of
+ * the form "Full Name <email-address>".  The returned array is owned
+ * by @extension and should not be modified or freed.
+ *
+ * Returns: a %NULL-terminated string array of Bcc recipients
+ *
+ * Since: 3.6
+ **/
+const gchar * const *
+e_source_mail_composition_get_bcc (ESourceMailComposition *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	return (const gchar * const *) extension->priv->bcc;
+}
+
+/**
+ * e_source_mail_composition_dup_bcc:
+ * @extension: an #ESourceMailComposition
+ *
+ * Thread-safe variation of e_source_mail_composition_get_bcc().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string array should be freed with g_strfreev() when no
+ * longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailComposition:bcc
+ *
+ * Since: 3.6
+ **/
+gchar **
+e_source_mail_composition_dup_bcc (ESourceMailComposition *extension)
+{
+	const gchar * const *protected;
+	gchar **duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_composition_get_bcc (extension);
+	duplicate = g_strdupv ((gchar **) protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_composition_set_bcc:
+ * @extension: an #ESource
+ * @bcc: a %NULL-terminated string array of Bcc recipients
+ *
+ * Sets the recipients which should automatically be added to the blind
+ * carbon-copy (Bcc) list when composing a new mail message.  The recipient
+ * strings should be of the form "Full Name <email-address>".
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_composition_set_bcc (ESourceMailComposition *extension,
+                                   const gchar * const *bcc)
+{
+	g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_strfreev (extension->priv->bcc);
+	extension->priv->bcc = g_strdupv ((gchar **) bcc);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "bcc");
+}
+
+/**
+ * e_source_mail_composition_get_cc:
+ * @extension: an #ESourceMailComposition
+ *
+ * Returns a %NULL-terminated string array of recipients which should
+ * automatically be added to the carbon-copy (Cc) list when composing a
+ * new mail message.  The recipient strings should be of the form "Full
+ * Name <email-address>".  The returned array is owned by @extension and
+ * should not be modified or freed.
+ *
+ * Returns: a %NULL-terminated string array of Cc recipients
+ *
+ * Since: 3.6
+ **/
+const gchar * const *
+e_source_mail_composition_get_cc (ESourceMailComposition *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	return (const gchar * const *) extension->priv->cc;
+}
+
+/**
+ * e_source_mail_composition_dup_cc:
+ * @extension: an #ESourceMailComposition
+ *
+ * Thread-safe variation of e_source_mail_composition_get_cc().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string array should be freed with g_strfreev() when no
+ * longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailComposition:cc
+ *
+ * Since: 3.6
+ **/
+gchar **
+e_source_mail_composition_dup_cc (ESourceMailComposition *extension)
+{
+	const gchar * const *protected;
+	gchar **duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_composition_get_cc (extension);
+	duplicate = g_strdupv ((gchar **) protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_composition_set_cc:
+ * @extension: an #ESourceMailComposition
+ * @cc: a %NULL-terminated string array of Cc recipients
+ *
+ * Sets the recipients which should automatically be added to the carbon
+ * copy (Cc) list when composing a new mail message.  The recipient strings
+ * should be of the form "Full Name <email-address>".
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_composition_set_cc (ESourceMailComposition *extension,
+                                  const gchar * const *cc)
+{
+	g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_strfreev (extension->priv->cc);
+	extension->priv->cc = g_strdupv ((gchar **) cc);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "cc");
+}
+
+/**
+ * e_source_mail_composition_get_drafts_folder:
+ * @extension: an #ESourceMailComposition
+ * 
+ * Returns a string identifying the preferred folder for draft messages.
+ * The format of the identifier string is defined by the client application.
+ *
+ * Returns: an identifier for the preferred drafts folder
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_composition_get_drafts_folder (ESourceMailComposition *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	return extension->priv->drafts_folder;
+}
+
+/**
+ * e_source_mail_composition_dup_drafts_folder:
+ * @extension: an #ESourceMailComposition
+ *
+ * Thread-safe variation of e_source_mail_composition_get_drafts_folder().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailComposition:drafts-folder
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_composition_dup_drafts_folder (ESourceMailComposition *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_composition_get_drafts_folder (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_composition_set_drafts_folder:
+ * @extension: an #ESourceMailComposition
+ * @drafts_folder: an identifier for the preferred drafts folder, or %NULL
+ *
+ * Sets the preferred folder for draft messages by an identifier string.
+ * The format of the identifier string is defined by the client application.
+ *
+ * The internal copy of @drafts_folder is automatically stripped of
+ * leading and trailing whitespace.  If the resulting string is empty,
+ * %NULL is set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_composition_set_drafts_folder (ESourceMailComposition *extension,
+                                             const gchar *drafts_folder)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (drafts_folder);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->drafts_folder);
+	extension->priv->drafts_folder = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "drafts-folder");
+}
+
+/**
+ * e_source_mail_composition_get_sign_imip:
+ * @extension: an #ESourceMailComposition
+ *
+ * Returns whether outgoing iMIP messages such as meeting requests should
+ * also be signed.  This is primarily intended as a workaround for certain
+ * versions of Microsoft Outlook which can't handle signed iMIP messages.
+ *
+ * Returns: whether outgoing iMIP messages should be signed
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_mail_composition_get_sign_imip (ESourceMailComposition *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), FALSE);
+
+	return extension->priv->sign_imip;
+}
+
+/**
+ * e_source_mail_composition_set_sign_imip:
+ * @extension: an #ESourceMailComposition
+ * @sign_imip: whether outgoing iMIP messages should be signed
+ *
+ * Sets whether outgoing iMIP messages such as meeting requests should
+ * also be signed.  This is primarily intended as a workaround for certain
+ * versions of Microsoft Outlook which can't handle signed iMIP messages.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_composition_set_sign_imip (ESourceMailComposition *extension,
+                                         gboolean sign_imip)
+{
+	g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension));
+
+	extension->priv->sign_imip = sign_imip;
+
+	g_object_notify (G_OBJECT (extension), "sign-imip");
+}
+
+/**
+ * e_source_mail_composition_get_templates_folder:
+ * @extension: an #ESourceMailComposition
+ *
+ * Returns a string identifying the preferred folder for message templates.
+ * The format of the identifier string is defined by the client application.
+ *
+ * Returns: an identifier for the preferred templates folder
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_composition_get_templates_folder (ESourceMailComposition *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	return extension->priv->templates_folder;
+}
+
+/**
+ * e_source_mail_composition_dup_templates_folder:
+ * @extension: an #ESourceMailComposition
+ *
+ * Thread-safe variation of e_source_mail_composition_get_templates_folder().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailComposition:templates-folder
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_composition_dup_templates_folder (ESourceMailComposition *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_composition_get_templates_folder (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_composition_set_templates_folder:
+ * @extension: an #ESourceMailComposition
+ * @templates_folder: an identifier for the preferred templates folder,
+ *                    or %NULL
+ *
+ * Sets the preferred folder for message templates by an identifier string.
+ * The format of the identifier string is defined by the client application.
+ *
+ * The internal copy of @templates_folder is automatically stripped of
+ * leading and trailing whitespace.  If the resulting string is empty,
+ * %NULL is set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_composition_set_templates_folder (ESourceMailComposition *extension,
+                                                const gchar *templates_folder)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (templates_folder);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->templates_folder);
+	extension->priv->templates_folder = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "templates-folder");
+}
+
diff --git a/libedataserver/e-source-mail-composition.h b/libedataserver/e-source-mail-composition.h
new file mode 100644
index 0000000..29c8ffd
--- /dev/null
+++ b/libedataserver/e-source-mail-composition.h
@@ -0,0 +1,116 @@
+/*
+ * e-source-mail-composition.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_SOURCE_MAIL_COMPOSITION_H
+#define E_SOURCE_MAIL_COMPOSITION_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MAIL_COMPOSITION \
+	(e_source_mail_composition_get_type ())
+#define E_SOURCE_MAIL_COMPOSITION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailComposition))
+#define E_SOURCE_MAIL_COMPOSITION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailCompositionClass))
+#define E_IS_SOURCE_MAIL_COMPOSITION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MAIL_COMPOSITION))
+#define E_IS_SOURCE_MAIL_COMPOSITION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MAIL_COMPOSITION))
+#define E_SOURCE_MAIL_COMPOSITION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailCompositionClass))
+
+/**
+ * E_SOURCE_EXTENSION_MAIL_COMPOSITION:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMailComposition.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MAIL_COMPOSITION "Mail Composition"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceMailComposition ESourceMailComposition;
+typedef struct _ESourceMailCompositionClass ESourceMailCompositionClass;
+typedef struct _ESourceMailCompositionPrivate ESourceMailCompositionPrivate;
+
+/**
+ * ESourceMailComposition:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMailComposition {
+	ESourceExtension parent;
+	ESourceMailCompositionPrivate *priv;
+};
+
+struct _ESourceMailCompositionClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_mail_composition_get_type
+					(void) G_GNUC_CONST;
+const gchar * const *
+		e_source_mail_composition_get_bcc
+					(ESourceMailComposition *extension);
+gchar **	e_source_mail_composition_dup_bcc
+					(ESourceMailComposition *extension);
+void		e_source_mail_composition_set_bcc
+					(ESourceMailComposition *extension,
+					 const gchar * const *bcc);
+const gchar * const *
+		e_source_mail_composition_get_cc
+					(ESourceMailComposition *extension);
+gchar **	e_source_mail_composition_dup_cc
+					(ESourceMailComposition *extension);
+void		e_source_mail_composition_set_cc
+					(ESourceMailComposition *extension,
+					 const gchar * const *cc);
+const gchar *	e_source_mail_composition_get_drafts_folder
+					(ESourceMailComposition *extension);
+gchar *		e_source_mail_composition_dup_drafts_folder
+					(ESourceMailComposition *extension);
+void		e_source_mail_composition_set_drafts_folder
+					(ESourceMailComposition *extension,
+					 const gchar *drafts_folder);
+gboolean	e_source_mail_composition_get_sign_imip
+					(ESourceMailComposition *extension);
+void		e_source_mail_composition_set_sign_imip
+					(ESourceMailComposition *extension,
+					 gboolean sign_imip);
+const gchar *	e_source_mail_composition_get_templates_folder
+					(ESourceMailComposition *extension);
+gchar *		e_source_mail_composition_dup_templates_folder
+					(ESourceMailComposition *extension);
+void		e_source_mail_composition_set_templates_folder
+					(ESourceMailComposition *extension,
+					 const gchar *templates_folder);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_MAIL_COMPOSITION_H */
diff --git a/libedataserver/e-source-mail-identity.c b/libedataserver/e-source-mail-identity.c
new file mode 100644
index 0000000..e4a59ea
--- /dev/null
+++ b/libedataserver/e-source-mail-identity.c
@@ -0,0 +1,722 @@
+/*
+ * e-source-mail-identity.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-mail-identity
+ * @include: libedataserver/e-source-mail-identity.h
+ * @short_description: #ESource extension for an email identity
+ *
+ * The #ESourceMailIdentity extension describes an "identity" for a mail
+ * account, which is the information that other people see when they read
+ * your messages.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-mail-identity.h>
+ *
+ *   ESourceMailIdentity *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
+ * ]|
+ **/
+
+#include "e-source-mail-identity.h"
+
+#include <libedataserver/e-source-mail-signature.h>
+
+#define E_SOURCE_MAIL_IDENTITY_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentityPrivate))
+
+struct _ESourceMailIdentityPrivate {
+	GMutex *property_lock;
+	gchar *address;
+	gchar *name;
+	gchar *organization;
+	gchar *reply_to;
+	gchar *signature_uid;
+};
+
+enum {
+	PROP_0,
+	PROP_ADDRESS,
+	PROP_NAME,
+	PROP_ORGANIZATION,
+	PROP_REPLY_TO,
+	PROP_SIGNATURE_UID
+};
+
+G_DEFINE_TYPE (
+	ESourceMailIdentity,
+	e_source_mail_identity,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_mail_identity_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ADDRESS:
+			e_source_mail_identity_set_address (
+				E_SOURCE_MAIL_IDENTITY (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_NAME:
+			e_source_mail_identity_set_name (
+				E_SOURCE_MAIL_IDENTITY (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_ORGANIZATION:
+			e_source_mail_identity_set_organization (
+				E_SOURCE_MAIL_IDENTITY (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_REPLY_TO:
+			e_source_mail_identity_set_reply_to (
+				E_SOURCE_MAIL_IDENTITY (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SIGNATURE_UID:
+			e_source_mail_identity_set_signature_uid (
+				E_SOURCE_MAIL_IDENTITY (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_identity_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ADDRESS:
+			g_value_take_string (
+				value,
+				e_source_mail_identity_dup_address (
+				E_SOURCE_MAIL_IDENTITY (object)));
+			return;
+
+		case PROP_NAME:
+			g_value_take_string (
+				value,
+				e_source_mail_identity_dup_name (
+				E_SOURCE_MAIL_IDENTITY (object)));
+			return;
+
+		case PROP_ORGANIZATION:
+			g_value_take_string (
+				value,
+				e_source_mail_identity_dup_organization (
+				E_SOURCE_MAIL_IDENTITY (object)));
+			return;
+
+		case PROP_REPLY_TO:
+			g_value_take_string (
+				value,
+				e_source_mail_identity_dup_reply_to (
+				E_SOURCE_MAIL_IDENTITY (object)));
+			return;
+
+		case PROP_SIGNATURE_UID:
+			g_value_take_string (
+				value,
+				e_source_mail_identity_dup_signature_uid (
+				E_SOURCE_MAIL_IDENTITY (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_identity_finalize (GObject *object)
+{
+	ESourceMailIdentityPrivate *priv;
+
+	priv = E_SOURCE_MAIL_IDENTITY_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->address);
+	g_free (priv->name);
+	g_free (priv->organization);
+	g_free (priv->reply_to);
+	g_free (priv->signature_uid);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_mail_identity_parent_class)->finalize (object);
+}
+
+static void
+e_source_mail_identity_class_init (ESourceMailIdentityClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceMailIdentityPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_mail_identity_set_property;
+	object_class->get_property = source_mail_identity_get_property;
+	object_class->finalize = source_mail_identity_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ADDRESS,
+		g_param_spec_string (
+			"address",
+			"Address",
+			"Sender's email address",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_NAME,
+		g_param_spec_string (
+			"name",
+			"Name",
+			"Sender's name",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ORGANIZATION,
+		g_param_spec_string (
+			"organization",
+			"Organization",
+			"Sender's organization",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_REPLY_TO,
+		g_param_spec_string (
+			"reply-to",
+			"Reply-To",
+			"Sender's reply-to address",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGNATURE_UID,
+		g_param_spec_string (
+			"signature-uid",
+			"Signature UID",
+			"ESource UID of the sender's signature",
+			"none",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_mail_identity_init (ESourceMailIdentity *extension)
+{
+	extension->priv = E_SOURCE_MAIL_IDENTITY_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_mail_identity_get_address:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Returns the email address for this identity from which to send messages.
+ * This may be an empty string but will never be %NULL.
+ *
+ * Returns: the sender's email address
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_identity_get_address (ESourceMailIdentity *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	return extension->priv->address;
+}
+
+/**
+ * e_source_mail_identity_dup_address:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Thread-safe variation of e_source_mail_identity_get_address().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailIdentity:address
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_identity_dup_address (ESourceMailIdentity *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_identity_get_address (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_identity_set_address:
+ * @extension: an #ESourceMailIdentity
+ * @address: the sender's email address, or %NULL
+ *
+ * Sets the email address for this identity from which to send messages.
+ *
+ * The internal copy of @address is automatically stripped of leading and
+ * trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_identity_set_address (ESourceMailIdentity *extension,
+                                    const gchar *address)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (address);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->address);
+	extension->priv->address = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "address");
+}
+
+/**
+ * e_source_mail_identity_get_name:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Returns the sender's name for this identity.
+ *
+ * Returns: the sender's name
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_identity_get_name (ESourceMailIdentity *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	return extension->priv->name;
+}
+
+/**
+ * e_source_mail_identity_dup_name:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Thread-safe variation of e_source_mail_identity_get_name().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailIdentity:name
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_identity_dup_name (ESourceMailIdentity *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_identity_get_name (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_identity_set_name:
+ * @extension: an #ESourceMailIdentity
+ * @name: the sender's name, or %NULL
+ *
+ * Sets the sender's name for this identity.
+ *
+ * The internal copy of @name is automatically stripped of leading and
+ * trailing whitespace.  If @name is %NULL or the resulting string is
+ * empty, the result of g_get_real_name() is set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_identity_set_name (ESourceMailIdentity *extension,
+                                 const gchar *name)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (name);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	/* Replace NULL with the user's real name. */
+	if (duplicate == NULL)
+		duplicate = g_strdup (g_get_real_name ());
+
+	g_free (extension->priv->name);
+	extension->priv->name = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "name");
+}
+
+/**
+ * e_source_mail_identity_get_organization:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Returns the sender's organization for this identity.
+ *
+ * Returns: the sender's organization
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_identity_get_organization (ESourceMailIdentity *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	return extension->priv->organization;
+}
+
+/**
+ * e_source_mail_identity_dup_organization:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Thread-safe variation of e_source_mail_identity_dup_organization().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailIdentity:organization
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_identity_dup_organization (ESourceMailIdentity *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_identity_get_organization (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_identity_set_organization:
+ * @extension: an #ESourceMailIdentity
+ * @organization: the sender's organization, or %NULL
+ *
+ * Sets the sender's organization for this identity.
+ *
+ * The internal copy of @organization is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_identity_set_organization (ESourceMailIdentity *extension,
+                                         const gchar *organization)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (organization);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->organization);
+	extension->priv->organization = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "organization");
+}
+
+/**
+ * e_source_mail_identity_get_reply_to:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Returns the email address for this identity to which recipients should
+ * send replies.
+ *
+ * Returns: the sender's reply-to address
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_identity_get_reply_to (ESourceMailIdentity *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	return extension->priv->reply_to;
+}
+
+/**
+ * e_source_mail_identity_dup_reply_to:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Thread-safe variation of e_source_mail_identity_get_reply_to().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailIdentity:reply-to
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_identity_dup_reply_to (ESourceMailIdentity *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_identity_get_reply_to (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_identity_set_reply_to:
+ * @extension: an #ESourceMailIdentity
+ * @reply_to: the sender's reply-to address, or %NULL
+ *
+ * Sets the email address for this identity to which recipients should
+ * send replies.
+ *
+ * The internal copy of @reply_to is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is
+ * set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_identity_set_reply_to (ESourceMailIdentity *extension,
+                                     const gchar *reply_to)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing writespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (reply_to);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->reply_to);
+	extension->priv->reply_to = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "reply-to");
+}
+
+/**
+ * e_source_mail_identity_get_signature_uid:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Returns the #ESource:uid of an #ESource describing a mail signature.
+ *
+ * If the user does not want to use a signature for this identity, the
+ * convention is to set the #ESourceMailIdentity:signature-uid property
+ * to "none".
+ *
+ * Returns: the sender's signature ID, or "none"
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_identity_get_signature_uid (ESourceMailIdentity *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	return extension->priv->signature_uid;
+}
+
+/**
+ * e_source_mail_identity_dup_signature_uid:
+ * @extension: an #ESourceMailIdentity
+ *
+ * Thread-safe variation of e_source_mail_identity_get_signature_uid().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailIdentity:signature-uid
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_identity_dup_signature_uid (ESourceMailIdentity *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_identity_get_signature_uid (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_identity_set_signature_uid:
+ * @extension: an #ESourceMailIdentity
+ * @signature_uid: the sender's signature ID, or %NULL
+ *
+ * Sets the #ESource:uid of an #ESource describing a mail signature.
+ *
+ * If the user does not want to use a signature for this identity, the
+ * convention is to set the #ESourceMailIdentity:signature-uid property
+ * to "none".  In keeping with that convention, the property will be set
+ * to "none" if @signature is %NULL or an empty string.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_identity_set_signature_uid (ESourceMailIdentity *extension,
+                                          const gchar *signature_uid)
+{
+	g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension));
+
+	/* Convert empty strings to "none". */
+	if (signature_uid == NULL || *signature_uid == '\0')
+		signature_uid = "none";
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_free (extension->priv->signature_uid);
+	extension->priv->signature_uid = g_strdup (signature_uid);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "signature-uid");
+}
+
diff --git a/libedataserver/e-source-mail-identity.h b/libedataserver/e-source-mail-identity.h
new file mode 100644
index 0000000..276fc84
--- /dev/null
+++ b/libedataserver/e-source-mail-identity.h
@@ -0,0 +1,116 @@
+/*
+ * e-source-mail-identity.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_SOURCE_MAIL_IDENTITY_H
+#define E_SOURCE_MAIL_IDENTITY_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MAIL_IDENTITY \
+	(e_source_mail_identity_get_type ())
+#define E_SOURCE_MAIL_IDENTITY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentity))
+#define E_SOURCE_MAIL_IDENTITY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentityClass))
+#define E_IS_SOURCE_MAIL_IDENTITY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MAIL_IDENTITY))
+#define E_IS_SOURCE_MAIL_IDENTITY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MAIL_IDENTITY))
+#define E_SOURCE_MAIL_IDENTITY_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentityClass))
+
+/**
+ * E_SOURCE_EXTENSION_MAIL_IDENTITY:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMailIdentity.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MAIL_IDENTITY "Mail Identity"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceMailIdentity ESourceMailIdentity;
+typedef struct _ESourceMailIdentityClass ESourceMailIdentityClass;
+typedef struct _ESourceMailIdentityPrivate ESourceMailIdentityPrivate;
+
+/**
+ * ESourceMailIdentity:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMailIdentity {
+	ESourceExtension parent;
+	ESourceMailIdentityPrivate *priv;
+};
+
+struct _ESourceMailIdentityClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_mail_identity_get_type
+					(void) G_GNUC_CONST;
+const gchar *	e_source_mail_identity_get_address
+					(ESourceMailIdentity *extension);
+gchar *		e_source_mail_identity_dup_address
+					(ESourceMailIdentity *extension);
+void		e_source_mail_identity_set_address
+					(ESourceMailIdentity *extension,
+					 const gchar *address);
+const gchar *	e_source_mail_identity_get_name
+					(ESourceMailIdentity *extension);
+gchar *		e_source_mail_identity_dup_name
+					(ESourceMailIdentity *extension);
+void		e_source_mail_identity_set_name
+					(ESourceMailIdentity *extension,
+					 const gchar *name);
+const gchar *	e_source_mail_identity_get_organization
+					(ESourceMailIdentity *extension);
+gchar *		e_source_mail_identity_dup_organization
+					(ESourceMailIdentity *extension);
+void		e_source_mail_identity_set_organization
+					(ESourceMailIdentity *extension,
+					 const gchar *organization);
+const gchar *	e_source_mail_identity_get_reply_to
+					(ESourceMailIdentity *extension);
+gchar *		e_source_mail_identity_dup_reply_to
+					(ESourceMailIdentity *extension);
+void		e_source_mail_identity_set_reply_to
+					(ESourceMailIdentity *extension,
+					 const gchar *reply_to);
+const gchar *	e_source_mail_identity_get_signature_uid
+					(ESourceMailIdentity *extension);
+gchar *		e_source_mail_identity_dup_signature_uid
+					(ESourceMailIdentity *extension);
+void		e_source_mail_identity_set_signature_uid
+					(ESourceMailIdentity *extension,
+					 const gchar *signature_uid);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_MAIL_IDENTITY_H */
diff --git a/libedataserver/e-source-mail-signature.c b/libedataserver/e-source-mail-signature.c
new file mode 100644
index 0000000..f07f29c
--- /dev/null
+++ b/libedataserver/e-source-mail-signature.c
@@ -0,0 +1,971 @@
+/*
+ * e-source-mail-signature.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-mail-signature
+ * @include: libedataserver/e-source-mail-signature.h
+ * @short_description: #ESource extension for email signatures
+ *
+ * The #ESourceMailSignature extension refers to a personalized email
+ * signature.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-mail-signature.h>
+ *
+ *   ESourceMailSignature *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_SIGNATURE);
+ * ]|
+ **/
+
+#include "e-source-mail-signature.h"
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/e-data-server-util.h>
+
+#define E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignaturePrivate))
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _ESourceMailSignaturePrivate {
+	GMutex *property_lock;
+	GFile *file;
+	gchar *mime_type;
+};
+
+struct _AsyncContext {
+	gchar *contents;
+	gchar *symlink_target;
+	gsize length;
+};
+
+enum {
+	PROP_0,
+	PROP_FILE,
+	PROP_MIME_TYPE
+};
+
+G_DEFINE_TYPE (
+	ESourceMailSignature,
+	e_source_mail_signature,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+	g_free (async_context->contents);
+	g_free (async_context->symlink_target);
+
+	g_slice_free (AsyncContext, async_context);
+}
+
+static void
+source_mail_signature_set_property (GObject *object,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_MIME_TYPE:
+			e_source_mail_signature_set_mime_type (
+				E_SOURCE_MAIL_SIGNATURE (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_signature_get_property (GObject *object,
+                                    guint property_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_FILE:
+			g_value_set_object (
+				value,
+				e_source_mail_signature_get_file (
+				E_SOURCE_MAIL_SIGNATURE (object)));
+			return;
+
+		case PROP_MIME_TYPE:
+			g_value_take_string (
+				value,
+				e_source_mail_signature_dup_mime_type (
+				E_SOURCE_MAIL_SIGNATURE (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_signature_dispose (GObject *object)
+{
+	ESourceMailSignaturePrivate *priv;
+
+	priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object);
+
+	if (priv->file != NULL) {
+		g_object_unref (priv->file);
+		priv->file = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_source_mail_signature_parent_class)->
+		dispose (object);
+}
+
+static void
+source_mail_signature_finalize (GObject *object)
+{
+	ESourceMailSignaturePrivate *priv;
+
+	priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->mime_type);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_mail_signature_parent_class)->
+		finalize (object);
+}
+
+static void
+source_mail_signature_constructed (GObject *object)
+{
+	ESourceMailSignaturePrivate *priv;
+	ESourceExtension *extension;
+	ESource *source;
+	const gchar *config_dir;
+	const gchar *uid;
+	gchar *base_dir;
+	gchar *path;
+
+	priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_source_mail_signature_parent_class)->
+		constructed (object);
+
+	extension = E_SOURCE_EXTENSION (object);
+	source = e_source_extension_get_source (extension);
+	uid = e_source_get_uid (source);
+
+	config_dir = e_get_user_config_dir ();
+	base_dir = g_build_filename (config_dir, "signatures", NULL);
+	path = g_build_filename (base_dir, uid, NULL);
+	priv->file = g_file_new_for_path (path);
+	g_mkdir_with_parents (base_dir, 0700);
+	g_free (base_dir);
+	g_free (path);
+}
+
+static void
+e_source_mail_signature_class_init (ESourceMailSignatureClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (
+		class, sizeof (ESourceMailSignaturePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_mail_signature_set_property;
+	object_class->get_property = source_mail_signature_get_property;
+	object_class->dispose = source_mail_signature_dispose;
+	object_class->finalize = source_mail_signature_finalize;
+	object_class->constructed = source_mail_signature_constructed;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FILE,
+		g_param_spec_object (
+			"file",
+			"File",
+			"File containing signature content",
+			G_TYPE_FILE,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_MIME_TYPE,
+		g_param_spec_string (
+			"mime-type",
+			"MIME Type",
+			"MIME type of the signature content",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_mail_signature_init (ESourceMailSignature *extension)
+{
+	extension->priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_mail_signature_get_file:
+ * @extension: an #ESourceMailSignature
+ *
+ * Returns a #GFile instance pointing to the signature file for @extension.
+ * The signature file may be a regular file containing the static signature
+ * content, or it may be a symbolic link to an executable file that produces
+ * the signature content.
+ *
+ * e_source_mail_signature_load() uses this to load the signature content.
+ *
+ * Returns: a #GFile
+ *
+ * Since: 3.6
+ **/
+GFile *
+e_source_mail_signature_get_file (ESourceMailSignature *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL);
+
+	return extension->priv->file;
+}
+
+/**
+ * e_source_mail_signature_get_mime_type:
+ * @extension: an #ESourceMailSignature
+ *
+ * Returns the MIME type of the signature content for @extension, or %NULL
+ * if it has not yet been determined.
+ *
+ * e_source_mail_signature_load() sets this automatically if the MIME type
+ * has not yet been determined.
+ *
+ * Returns: the MIME type of the signature content, or %NULL
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_signature_get_mime_type (ESourceMailSignature *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL);
+
+	return extension->priv->mime_type;
+}
+
+/**
+ * e_source_mail_signature_dup_mime_type:
+ * @extension: an #ESourceMailSignature
+ *
+ * Thread-safe variation of e_source_mail_signature_get_mime_type().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailSignature:mime-type
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_signature_dup_mime_type (ESourceMailSignature *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_signature_get_mime_type (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_signature_set_mime_type:
+ * @extension: an #ESourceMailSignature
+ * @mime_type: a MIME type, or %NULL
+ *
+ * Sets the MIME type of the signature content for @extension.
+ *
+ * e_source_mail_signature_load() sets this automatically if the MIME type
+ * has not yet been determined.
+ *
+ * The internal copy of @mime_type is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is
+ * set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_signature_set_mime_type (ESourceMailSignature *extension,
+                                       const gchar *mime_type)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (mime_type);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->mime_type);
+	extension->priv->mime_type = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "mime-type");
+}
+
+/********************** e_source_mail_signature_load() ***********************/
+
+/* Helper for e_source_mail_signature_load() */
+static void
+source_mail_signature_load_thread (GSimpleAsyncResult *simple,
+                                   GObject *object,
+                                   GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_mail_signature_load_sync (
+		E_SOURCE (object),
+		&async_context->contents,
+		&async_context->length,
+		cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/**
+ * e_source_mail_signature_load_sync:
+ * @source: an #ESource
+ * @contents: return location for the signature content
+ * @length: return location for the length of the signature content,
+ *          or %NULL if the length is not needed
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Loads a signature from the signature file for @source, which is
+ * given by e_source_mail_signature_get_file().  The signature contents
+ * are placed in @contents, and @length is set to the size of the @contents
+ * string.  The @contents string should be freed with g_free() when no
+ * longer needed.
+ *
+ * If the signature file is executable, it will be executed and its output
+ * captured as the email signature content.  If the signature file is not
+ * executable, the email signature content is read directly from the file.
+ *
+ * Returns; %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_mail_signature_load_sync (ESource *source,
+                                   gchar **contents,
+                                   gsize *length,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+	ESourceMailSignature *extension;
+	GFileInfo *file_info;
+	GFile *file;
+	const gchar *content_type;
+	const gchar *extension_name;
+	gchar *local_contents = NULL;
+	gboolean can_execute;
+	gboolean success;
+	gchar *guessed_content_type;
+	gchar *command_line;
+	gchar *mime_type;
+	gchar *path;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (contents != NULL, FALSE);
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
+	extension = e_source_get_extension (source, extension_name);
+	file = e_source_mail_signature_get_file (extension);
+
+	file_info = g_file_query_info (
+		file,
+		G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ","
+		G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+		G_FILE_QUERY_INFO_NONE,
+		cancellable, error);
+
+	if (file_info == NULL)
+		return FALSE;
+
+	can_execute = g_file_info_get_attribute_boolean (
+		file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
+
+	content_type = g_file_info_get_content_type (file_info);
+	mime_type = g_content_type_get_mime_type (content_type);
+
+	if (can_execute)
+		goto execute;
+
+	/*** Load signature file contents ***/
+
+	success = g_file_load_contents (
+		file, cancellable, &local_contents, NULL, NULL, error);
+
+	if (!success)
+		goto exit;
+
+	g_return_val_if_fail (local_contents != NULL, FALSE);
+
+	/* Signatures are saved as UTF-8, but we still need to check that
+	 * the signature is valid UTF-8 because the user may be opening a
+	 * signature file this is in his/her locale character set.  If it
+	 * is not UTF-8 then try converting from the current locale. */
+	if (!g_utf8_validate (local_contents, -1, NULL)) {
+		gchar *utf8;
+
+		utf8 = g_locale_to_utf8 (
+			local_contents, -1, NULL, NULL, error);
+
+		if (utf8 == NULL) {
+			success = FALSE;
+			goto exit;
+		}
+
+		g_free (local_contents);
+		local_contents = utf8;
+	}
+
+	goto exit;
+
+execute:
+
+	/*** Execute signature file and capture output ***/
+
+	path = g_file_get_path (file);
+
+	if (path == NULL) {
+		g_set_error (
+			error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Signature script must be a local file"));
+		success = FALSE;
+		goto exit;
+	}
+
+	/* Enclose the path in single-quotes for compatibility on Windows.
+	 * (See g_spawn_command_line_sync() documentation for rationale.) */
+	command_line = g_strdup_printf ("'%s'", path);
+
+	success = g_spawn_command_line_sync (
+		command_line, &local_contents, NULL, NULL, error);
+
+	g_free (command_line);
+	g_free (path);
+
+	/* Check if we failed to spawn the script. */
+	if (!success)
+		goto exit;
+
+	/* Check if we were cancelled while the script was running. */
+	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+		success = FALSE;
+		goto exit;
+	}
+
+	g_return_val_if_fail (local_contents != NULL, FALSE);
+
+	/* Signature scripts are supposed to generate UTF-8 content, but
+	 * because users are known to never read the manual, we try to do
+	 * our best if the content isn't valid UTF-8 by assuming that the
+	 * content is in the user's locale character set. */
+	if (!g_utf8_validate (local_contents, -1, NULL)) {
+		gchar *utf8;
+
+		utf8 = g_locale_to_utf8 (
+			local_contents, -1, NULL, NULL, error);
+
+		if (utf8 == NULL) {
+			success = FALSE;
+			goto exit;
+		}
+
+		g_free (local_contents);
+		local_contents = utf8;
+	}
+
+	g_free (mime_type);
+
+	/* Try and guess the content type of the script output
+	 * so it can be applied correctly to the mail message. */
+	guessed_content_type = g_content_type_guess (
+		NULL, (guchar *) local_contents,
+		strlen (local_contents), NULL);
+	mime_type = g_content_type_get_mime_type (guessed_content_type);
+	g_free (guessed_content_type);
+
+exit:
+	if (success) {
+		const gchar *ext_mime_type;
+
+		if (length != NULL)
+			*length = strlen (local_contents);
+
+		*contents = local_contents;
+		local_contents = NULL;
+
+		ext_mime_type =
+			e_source_mail_signature_get_mime_type (extension);
+
+		/* Don't override the MIME type if it's already set. */
+		if (ext_mime_type == NULL || *ext_mime_type == '\0')
+			e_source_mail_signature_set_mime_type (
+				extension, mime_type);
+	}
+
+	g_object_unref (file_info);
+	g_free (local_contents);
+	g_free (mime_type);
+
+	return success;
+}
+
+/**
+ * e_source_mail_signature_load:
+ * @source: an #ESource
+ * @io_priority: the I/O priority of the request
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously loads a signature from the signature file for @source,
+ * which is given by e_source_mail_signature_get_file().
+ *
+ * If the signature file is executable, it will be executed and its output
+ * captured as the email signature content.  If the signature file is not
+ * executable, the email signature content is read directly from the file.
+ *
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_source_mail_signature_load_finish() to get the result of
+ * the operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_signature_load (ESource *source,
+                              gint io_priority,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_if_fail (E_IS_SOURCE (source));
+
+	async_context = g_slice_new0 (AsyncContext);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (source), callback, user_data,
+		e_source_mail_signature_load);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_mail_signature_load_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_mail_signature_load_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @contents: return location for the signature content
+ * @length: return location for the length of the signature content,
+ *          or %NULL if the length is not needed
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an operation started with e_source_mail_signature_load().  The
+ * signature file contents are placed in @contents, and @length is set to
+ * the size of the @contents string.  The @contents string should be freed
+ * with g_free() when no longer needed.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_mail_signature_load_finish (ESource *source,
+                                     GAsyncResult *result,
+                                     gchar **contents,
+                                     gsize *length,
+                                     GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (source),
+		e_source_mail_signature_load), FALSE);
+
+	g_return_val_if_fail (contents != NULL, FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+
+	g_return_val_if_fail (async_context->contents != NULL, FALSE);
+
+	*contents = async_context->contents;
+	async_context->contents = NULL;
+
+	if (length != NULL)
+		*length = async_context->length;
+
+	return TRUE;
+}
+
+/********************* e_source_mail_signature_replace() *********************/
+
+/* Helper for e_source_mail_signature_replace() */
+static void
+source_mail_signature_replace_thread (GSimpleAsyncResult *simple,
+                                      GObject *object,
+                                      GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_mail_signature_replace_sync (
+		E_SOURCE (object), async_context->contents,
+		async_context->length, cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/**
+ * e_source_mail_signature_replace_sync:
+ * @source: an #ESource
+ * @contents: the signature contents
+ * @length: the length of @contents in bytes
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Replaces the signature file for @source with the given @contents
+ * of @length bytes.  The signature file for @source is given by
+ * e_source_mail_signature_get_file().
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_mail_signature_replace_sync (ESource *source,
+                                      const gchar *contents,
+                                      gsize length,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+	ESourceMailSignature *extension;
+	const gchar *extension_name;
+	GFile *file;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (contents != NULL, FALSE);
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
+	extension = e_source_get_extension (source, extension_name);
+	file = e_source_mail_signature_get_file (extension);
+
+	return g_file_replace_contents (
+		file, contents, length, NULL, FALSE,
+		G_FILE_CREATE_REPLACE_DESTINATION,
+		NULL, cancellable, error);
+}
+
+/**
+ * e_source_mail_signature_replace:
+ * @source: an #ESource
+ * @contents: the signature contents
+ * @length: the length of @contents in bytes
+ * @io_priority: the I/O priority of the request
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchrously replaces the signature file for @source with the given
+ * @contents of @length bytes.  The signature file for @source is given
+ * by e_source_mail_signature_get_file().
+ *
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_source_mail_signature_replace_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_signature_replace (ESource *source,
+                                 const gchar *contents,
+                                 gsize length,
+                                 gint io_priority,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (contents != NULL);
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->contents = g_strdup (contents);
+	async_context->length = length;
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (source), callback, user_data,
+		e_source_mail_signature_replace);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_mail_signature_replace_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_mail_signature_replace_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an operation started with e_source_mail_signature_replace().
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_mail_signature_replace_finish (ESource *source,
+                                        GAsyncResult *result,
+                                        GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (source),
+		e_source_mail_signature_replace), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/********************* e_source_mail_signature_symlink() *********************/
+
+/* Helper for e_source_mail_signature_symlink() */
+static void
+source_mail_signature_symlink_thread (GSimpleAsyncResult *simple,
+                                      GObject *object,
+                                      GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_mail_signature_symlink_sync (
+		E_SOURCE (object),
+		async_context->symlink_target,
+		cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/**
+ * e_source_mail_signature_symlink_sync:
+ * @source: an #ESource
+ * @symlink_target: executable filename to link to
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Replaces the signature file for @source with a symbolic link to
+ * @symlink_target, which should be an executable file that prints
+ * a mail signature to standard output.  The signature file for
+ * @source is given by e_source_mail_signature_get_file().
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_mail_signature_symlink_sync (ESource *source,
+                                      const gchar *symlink_target,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+	ESourceMailSignature *extension;
+	const gchar *extension_name;
+	GFile *file;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (symlink_target != NULL, FALSE);
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
+	extension = e_source_get_extension (source, extension_name);
+	file = e_source_mail_signature_get_file (extension);
+
+	/* The file may not exist, so we don't care if this fails.
+	 * If it fails for a different reason than G_IO_ERROR_NOT_FOUND
+	 * then the next step will probably also fail and we'll capture
+	 * THAT error. */
+	g_file_delete (file, cancellable, NULL);
+
+	return g_file_make_symbolic_link (
+		file, symlink_target, cancellable, error);
+}
+
+/**
+ * e_source_mail_signature_symlink:
+ * @source: an #ESource
+ * @symlink_target: executable filename to link to
+ * @io_priority: the I/O priority of the request
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously replaces the signature file for @source with a symbolic
+ * link to @symlink_target, which should be an executable file that prints
+ * a mail signature to standard output.  The signature file for @source
+ * is given by e_source_mail_signature_get_file().
+ *
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_source_mail_signature_symlink_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_signature_symlink (ESource *source,
+                                 const gchar *symlink_target,
+                                 gint io_priority,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (symlink_target != NULL);
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->symlink_target = g_strdup (symlink_target);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (source), callback, user_data,
+		e_source_mail_signature_symlink);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_mail_signature_symlink_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_mail_signature_symlink_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an operation started with e_source_mail_signature_symlink().
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_mail_signature_symlink_finish (ESource *source,
+                                        GAsyncResult *result,
+                                        GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (source),
+		e_source_mail_signature_symlink), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
diff --git a/libedataserver/e-source-mail-signature.h b/libedataserver/e-source-mail-signature.h
new file mode 100644
index 0000000..3922f8f
--- /dev/null
+++ b/libedataserver/e-source-mail-signature.h
@@ -0,0 +1,143 @@
+/*
+ * e-source-mail-signature.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_SOURCE_MAIL_SIGNATURE_H
+#define E_SOURCE_MAIL_SIGNATURE_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MAIL_SIGNATURE \
+	(e_source_mail_signature_get_type ())
+#define E_SOURCE_MAIL_SIGNATURE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignature))
+#define E_SOURCE_MAIL_SIGNATURE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignatureClass))
+#define E_IS_SOURCE_MAIL_SIGNATURE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MAIL_SIGNATURE))
+#define E_IS_SOURCE_MAIL_SIGNATURE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MAIL_SIGNATURE))
+#define E_SOURCE_MAIL_SIGNATURE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignatureClass))
+
+/**
+ * E_SOURCE_EXTENSION_MAIL_SIGNATURE:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMailSignature.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MAIL_SIGNATURE "Mail Signature"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceMailSignature ESourceMailSignature;
+typedef struct _ESourceMailSignatureClass ESourceMailSignatureClass;
+typedef struct _ESourceMailSignaturePrivate ESourceMailSignaturePrivate;
+
+/**
+ * ESourceMailSignature:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * function below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMailSignature {
+	ESourceExtension parent;
+	ESourceMailSignaturePrivate *priv;
+};
+
+struct _ESourceMailSignatureClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_mail_signature_get_type
+					(void) G_GNUC_CONST;
+GFile *		e_source_mail_signature_get_file
+					(ESourceMailSignature *extension);
+const gchar *	e_source_mail_signature_get_mime_type
+					(ESourceMailSignature *extension);
+gchar *		e_source_mail_signature_dup_mime_type
+					(ESourceMailSignature *extension);
+void		e_source_mail_signature_set_mime_type
+					(ESourceMailSignature *extension,
+					 const gchar *mime_type);
+
+gboolean	e_source_mail_signature_load_sync
+					(ESource *source,
+					 gchar **contents,
+					 gsize *length,
+					 GCancellable *cancellable,
+					 GError **error);
+void		e_source_mail_signature_load
+					(ESource *source,
+					 gint io_priority,
+					 GCancellable *cancellable,
+					 GAsyncReadyCallback callback,
+					 gpointer user_data);
+gboolean	e_source_mail_signature_load_finish
+					(ESource *source,
+					 GAsyncResult *result,
+					 gchar **contents,
+					 gsize *length,
+					 GError **error);
+gboolean	e_source_mail_signature_replace_sync
+					(ESource *source,
+					 const gchar *contents,
+					 gsize length,
+					 GCancellable *cancellable,
+					 GError **error);
+void		e_source_mail_signature_replace
+					(ESource *source,
+					 const gchar *contents,
+					 gsize length,
+					 gint io_priority,
+					 GCancellable *cancellable,
+					 GAsyncReadyCallback callback,
+					 gpointer user_data);
+gboolean	e_source_mail_signature_replace_finish
+					(ESource *source,
+					 GAsyncResult *result,
+					 GError **error);
+gboolean	e_source_mail_signature_symlink_sync
+					(ESource *source,
+					 const gchar *symlink_target,
+					 GCancellable *cancellable,
+					 GError **error);
+void		e_source_mail_signature_symlink
+					(ESource *source,
+					 const gchar *symlink_target,
+					 gint io_priority,
+					 GCancellable *cancellable,
+					 GAsyncReadyCallback callback,
+					 gpointer user_data);
+gboolean	e_source_mail_signature_symlink_finish
+					(ESource *source,
+					 GAsyncResult *result,
+					 GError **error);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_MAIL_SIGNATURE_H */
diff --git a/libedataserver/e-source-mail-submission.c b/libedataserver/e-source-mail-submission.c
new file mode 100644
index 0000000..5377c61
--- /dev/null
+++ b/libedataserver/e-source-mail-submission.c
@@ -0,0 +1,346 @@
+/*
+ * e-source-mail-submission.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-mail-submission
+ * @include: libedataserver/e-source-mail-submission.h
+ * @short_description: #ESource extension for submitting emails
+ *
+ * The #ESourceMailSubmission extension tracks settings to be applied
+ * when submitting a mail message for delivery.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-mail-submission.h>
+ *
+ *   ESourceMailSubmission *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_SUBMISSION);
+ * ]|
+ **/
+
+#include "e-source-mail-submission.h"
+
+#include <libedataserver/e-source-mail-transport.h>
+
+#define E_SOURCE_MAIL_SUBMISSION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmissionPrivate))
+
+struct _ESourceMailSubmissionPrivate {
+	GMutex *property_lock;
+	gchar *sent_folder;
+	gchar *transport_uid;
+};
+
+enum {
+	PROP_0,
+	PROP_SENT_FOLDER,
+	PROP_TRANSPORT_UID
+};
+
+G_DEFINE_TYPE (
+	ESourceMailSubmission,
+	e_source_mail_submission,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_mail_submission_set_property (GObject *object,
+                                     guint property_id,
+                                     const GValue *value,
+                                     GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SENT_FOLDER:
+			e_source_mail_submission_set_sent_folder (
+				E_SOURCE_MAIL_SUBMISSION (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_TRANSPORT_UID:
+			e_source_mail_submission_set_transport_uid (
+				E_SOURCE_MAIL_SUBMISSION (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_submission_get_property (GObject *object,
+                                     guint property_id,
+                                     GValue *value,
+                                     GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SENT_FOLDER:
+			g_value_take_string (
+				value,
+				e_source_mail_submission_dup_sent_folder (
+				E_SOURCE_MAIL_SUBMISSION (object)));
+			return;
+
+		case PROP_TRANSPORT_UID:
+			g_value_take_string (
+				value,
+				e_source_mail_submission_dup_transport_uid (
+				E_SOURCE_MAIL_SUBMISSION (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mail_submission_finalize (GObject *object)
+{
+	ESourceMailSubmissionPrivate *priv;
+
+	priv = E_SOURCE_MAIL_SUBMISSION_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->sent_folder);
+	g_free (priv->transport_uid);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_mail_submission_parent_class)->
+		finalize (object);
+}
+
+static void
+e_source_mail_submission_class_init (ESourceMailSubmissionClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (
+		class, sizeof (ESourceMailSubmissionPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_mail_submission_set_property;
+	object_class->get_property = source_mail_submission_get_property;
+	object_class->finalize = source_mail_submission_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SENT_FOLDER,
+		g_param_spec_string (
+			"sent-folder",
+			"Sent Folder",
+			"Preferred folder for sent messages",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_TRANSPORT_UID,
+		g_param_spec_string (
+			"transport-uid",
+			"Transport UID",
+			"ESource UID of a Mail Transport",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_mail_submission_init (ESourceMailSubmission *extension)
+{
+	extension->priv = E_SOURCE_MAIL_SUBMISSION_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_mail_submission_get_sent_folder:
+ * @extension: an #ESourceMailSubmission
+ *
+ * Returns a string identifying the preferred folder for sent messages.
+ * The format of the identifier string is defined by the client application.
+ *
+ * Returns: an identifier for the preferred sent folder
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_submission_get_sent_folder (ESourceMailSubmission *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL);
+
+	return extension->priv->sent_folder;
+}
+
+/**
+ * e_source_mail_submission_dup_sent_folder:
+ * @extension: an #ESourceMailSubmission
+ *
+ * Thread-safe variation of e_source_mail_submission_get_sent_folder().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailSubmission:sent-folder
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_submission_dup_sent_folder (ESourceMailSubmission *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_submission_get_sent_folder (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_submission_set_sent_folder:
+ * @extension: an #ESourceMailSubmission
+ * @sent_folder: an identifier for the preferred sent folder, or %NULL
+ *
+ * Sets the preferred folder for sent messages by an identifier string.
+ * The format of the identifier string is defined by the client application.
+ *
+ * The internal copy of @sent_folder is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_submission_set_sent_folder (ESourceMailSubmission *extension,
+                                          const gchar *sent_folder)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (sent_folder);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->sent_folder);
+	extension->priv->sent_folder = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "sent-folder");
+}
+
+/**
+ * e_source_mail_submission_get_transport_uid:
+ * @extension: an #ESourceMailSubmission
+ *
+ * Returns the #ESource:uid of the #ESource that describes the mail
+ * transport to be used for outgoing messages.
+ *
+ * Returns: the mail transport #ESource:uid
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_mail_submission_get_transport_uid (ESourceMailSubmission *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL);
+
+	return extension->priv->transport_uid;
+}
+
+/**
+ * e_source_mail_submission_dup_transport_uid:
+ * @extension: an #ESourceMailSubmission
+ *
+ * Thread-safe variation of e_source_mail_submission_get_transport_uid().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceMailSubmission:transport-uid
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_mail_submission_dup_transport_uid (ESourceMailSubmission *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_mail_submission_get_transport_uid (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_mail_submission_set_transport_uid:
+ * @extension: an #ESourceMailSubmission
+ * @transport_uid: the mail transport #ESource:uid, or %NULL
+ *
+ * Sets the #ESource:uid of the #ESource that describes the mail
+ * transport to be used for outgoing messages.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mail_submission_set_transport_uid (ESourceMailSubmission *extension,
+                                            const gchar *transport_uid)
+{
+	g_return_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_free (extension->priv->transport_uid);
+	extension->priv->transport_uid = g_strdup (transport_uid);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "transport-uid");
+}
diff --git a/libedataserver/e-source-mail-submission.h b/libedataserver/e-source-mail-submission.h
new file mode 100644
index 0000000..0833609
--- /dev/null
+++ b/libedataserver/e-source-mail-submission.h
@@ -0,0 +1,95 @@
+/*
+ * e-source-mail-submission.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_SOURCE_MAIL_SUBMISSION_H
+#define E_SOURCE_MAIL_SUBMISSION_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MAIL_SUBMISSION \
+	(e_source_mail_submission_get_type ())
+#define E_SOURCE_MAIL_SUBMISSION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmission))
+#define E_SOURCE_MAIL_SUBMISSION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmissionClass))
+#define E_IS_SOURCE_MAIL_SUBMISSION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MAIL_SUBMISSION))
+#define E_IS_SOURCE_MAIL_SUBMISSION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MAIL_SUBMISSION))
+#define E_SOURCE_MAIL_SUBMISSION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmissionClass))
+
+/**
+ * E_SOURCE_EXTENSION_MAIL_SUBMISSION:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMailSubmission.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MAIL_SUBMISSION "Mail Submission"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceMailSubmission ESourceMailSubmission;
+typedef struct _ESourceMailSubmissionClass ESourceMailSubmissionClass;
+typedef struct _ESourceMailSubmissionPrivate ESourceMailSubmissionPrivate;
+
+/**
+ * ESourceMailSubmission:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * function below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMailSubmission {
+	ESourceExtension parent;
+	ESourceMailSubmissionPrivate *priv;
+};
+
+struct _ESourceMailSubmissionClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_mail_submission_get_type
+					(void) G_GNUC_CONST;
+const gchar *	e_source_mail_submission_get_sent_folder
+					(ESourceMailSubmission *extension);
+gchar *		e_source_mail_submission_dup_sent_folder
+					(ESourceMailSubmission *extension);
+void		e_source_mail_submission_set_sent_folder
+					(ESourceMailSubmission *extension,
+					 const gchar *sent_folder);
+const gchar *	e_source_mail_submission_get_transport_uid
+					(ESourceMailSubmission *extension);
+gchar *		e_source_mail_submission_dup_transport_uid
+					(ESourceMailSubmission *extension);
+void		e_source_mail_submission_set_transport_uid
+					(ESourceMailSubmission *extension,
+					 const gchar *transport_uid);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_MAIL_SUBMISSION_H */
diff --git a/libedataserver/e-source-mail-transport.c b/libedataserver/e-source-mail-transport.c
new file mode 100644
index 0000000..d54f993
--- /dev/null
+++ b/libedataserver/e-source-mail-transport.c
@@ -0,0 +1,62 @@
+/*
+ * e-source-mail-transport.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-mail-transport
+ * @include: libedataserver/e-source-mail-transport.h
+ * @short_description: #ESource extension for an email transport
+ *
+ * The #ESourceMailTransport extension identifies the #ESource as a
+ * mail transport which describes where to send outgoing messages.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-mail-transport.h>
+ *
+ *   ESourceMailTransport *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT);
+ * ]|
+ **/
+
+#include "e-source-mail-transport.h"
+
+#define E_SOURCE_MAIL_TRANSPORT_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransportPrivate))
+
+G_DEFINE_TYPE (
+	ESourceMailTransport,
+	e_source_mail_transport,
+	E_TYPE_SOURCE_BACKEND)
+
+static void
+e_source_mail_transport_class_init (ESourceMailTransportClass *class)
+{
+	ESourceExtensionClass *extension_class;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
+}
+
+static void
+e_source_mail_transport_init (ESourceMailTransport *extension)
+{
+}
+
diff --git a/libedataserver/e-source-mail-transport.h b/libedataserver/e-source-mail-transport.h
new file mode 100644
index 0000000..de55111
--- /dev/null
+++ b/libedataserver/e-source-mail-transport.h
@@ -0,0 +1,81 @@
+/*
+ * e-source-mail-transport.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_SOURCE_MAIL_TRANSPORT_H
+#define E_SOURCE_MAIL_TRANSPORT_H
+
+#include <libedataserver/e-source-backend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MAIL_TRANSPORT \
+	(e_source_mail_transport_get_type ())
+#define E_SOURCE_MAIL_TRANSPORT(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransport))
+#define E_SOURCE_MAIL_TRANSPORT_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransportClass))
+#define E_IS_SOURCE_MAIL_TRANSPORT(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MAIL_TRANSPORT))
+#define E_IS_SOURCE_MAIL_TRANSPORT_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MAIL_TRANSPORT))
+#define E_SOURCE_MAIL_TRANSPORT_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransportClass))
+
+/**
+ * E_SOURCE_EXTENSION_MAIL_TRANSPORT:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMailTransport.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MAIL_TRANSPORT "Mail Transport"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceMailTransport ESourceMailTransport;
+typedef struct _ESourceMailTransportClass ESourceMailTransportClass;
+typedef struct _ESourceMailTransportPrivate ESourceMailTransportPrivate;
+
+/**
+ * ESourceMailTransport:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMailTransport {
+	ESourceBackend parent;
+	ESourceMailTransportPrivate *priv;
+};
+
+struct _ESourceMailTransportClass {
+	ESourceBackendClass parent_class;
+};
+
+GType		e_source_mail_transport_get_type
+						(void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* E_SOURCE_MAIL_TRANSPORT_H */
diff --git a/libedataserver/e-source-mdn.c b/libedataserver/e-source-mdn.c
new file mode 100644
index 0000000..e63d645
--- /dev/null
+++ b/libedataserver/e-source-mdn.c
@@ -0,0 +1,172 @@
+/*
+ * e-source-mdn.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-mdn
+ * @include: libedataserver/e-source-mdn.h
+ * @short_description: #ESource extension for MDN settings
+ *
+ * The #ESourceMDN extension tracks Message Disposition Notification
+ * settings for a mail account.  See RFC 2298 for more information about
+ * Message Disposition Notification.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-mdn.h>
+ *
+ *   ESourceMDN *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MDN);
+ * ]|
+ **/
+
+#include "e-source-mdn.h"
+
+#include <libedataserver/e-source-enumtypes.h>
+
+#define E_SOURCE_MDN_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_MDN, ESourceMDNPrivate))
+
+struct _ESourceMDNPrivate {
+	EMdnResponsePolicy response_policy;
+};
+
+enum {
+	PROP_0,
+	PROP_RESPONSE_POLICY
+};
+
+G_DEFINE_TYPE (
+	ESourceMDN,
+	e_source_mdn,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_mdn_set_property (GObject *object,
+                         guint property_id,
+                         const GValue *value,
+                         GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_RESPONSE_POLICY:
+			e_source_mdn_set_response_policy (
+				E_SOURCE_MDN (object),
+				g_value_get_enum (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_mdn_get_property (GObject *object,
+                         guint property_id,
+                         GValue *value,
+                         GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_RESPONSE_POLICY:
+			g_value_set_enum (
+				value,
+				e_source_mdn_get_response_policy (
+				E_SOURCE_MDN (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_source_mdn_class_init (ESourceMDNClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceMDNPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_mdn_set_property;
+	object_class->get_property = source_mdn_get_property;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_MDN;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_RESPONSE_POLICY,
+		g_param_spec_enum (
+			"response-policy",
+			"Response Policy",
+			"Policy for responding to MDN requests",
+			E_TYPE_MDN_RESPONSE_POLICY,
+			E_MDN_RESPONSE_POLICY_ASK,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_mdn_init (ESourceMDN *extension)
+{
+	extension->priv = E_SOURCE_MDN_GET_PRIVATE (extension);
+}
+
+/**
+ * e_source_mdn_get_response_policy:
+ * @extension: an #ESourceMDN
+ *
+ * Returns the policy for this mail account on responding to Message
+ * Disposition Notification requests when receiving mail messages.
+ *
+ * Returns: the #EMdnResponsePolicy for this account
+ *
+ * Since: 3.6
+ **/
+EMdnResponsePolicy
+e_source_mdn_get_response_policy (ESourceMDN *extension)
+{
+	g_return_val_if_fail (
+		E_IS_SOURCE_MDN (extension),
+		E_MDN_RESPONSE_POLICY_NEVER);
+
+	return extension->priv->response_policy;
+}
+
+/**
+ * e_source_mdn_set_response_policy:
+ * @extension: an #ESourceMDN
+ * @response_policy: the #EMdnResponsePolicy
+ *
+ * Sets the policy for this mail account on responding to Message
+ * Disposition Notification requests when receiving mail messages.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_mdn_set_response_policy (ESourceMDN *extension,
+                                  EMdnResponsePolicy response_policy)
+{
+	g_return_if_fail (E_IS_SOURCE_MDN (extension));
+
+	extension->priv->response_policy = response_policy;
+
+	g_object_notify (G_OBJECT (extension), "response-policy");
+}
diff --git a/libedataserver/e-source-mdn.h b/libedataserver/e-source-mdn.h
new file mode 100644
index 0000000..016ef7d
--- /dev/null
+++ b/libedataserver/e-source-mdn.h
@@ -0,0 +1,88 @@
+/*
+ * e-source-mdn.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_SOURCE_MDN_H
+#define E_SOURCE_MDN_H
+
+#include <libedataserver/e-source-enums.h>
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_MDN \
+	(e_source_mdn_get_type ())
+#define E_SOURCE_MDN(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_MDN, ESourceMDN))
+#define E_SOURCE_MDN_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_MDN, ESourceMDNClass))
+#define E_IS_SOURCE_MDN(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_MDN))
+#define E_IS_SOURCE_MDN_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_MDN))
+#define E_SOURCE_MDN_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_MDN, ESourceMDNClass))
+
+/**
+ * E_SOURCE_EXTENSION_MDN:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceMDN.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_MDN "Message Disposition Notifications"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceMDN ESourceMDN;
+typedef struct _ESourceMDNClass ESourceMDNClass;
+typedef struct _ESourceMDNPrivate ESourceMDNPrivate;
+
+/**
+ * ESourceMDN:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceMDN {
+	ESourceExtension parent;
+	ESourceMDNPrivate *priv;
+};
+
+struct _ESourceMDNClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_mdn_get_type
+					(void) G_GNUC_CONST;
+EMdnResponsePolicy
+		e_source_mdn_get_response_policy
+					(ESourceMDN *extension);
+void		e_source_mdn_set_response_policy
+					(ESourceMDN *extension,
+					 EMdnResponsePolicy response_policy);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_MDN_H */
diff --git a/libedataserver/e-source-offline.c b/libedataserver/e-source-offline.c
new file mode 100644
index 0000000..b5f60f4
--- /dev/null
+++ b/libedataserver/e-source-offline.c
@@ -0,0 +1,168 @@
+/*
+ * e-source-offline.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-offline
+ * @include: libedataserver/e-source-offline.h
+ * @short_description: #ESource extension for offline settings
+ *
+ * The #ESourceOffline extension tracks whether data from a remote
+ * server should be cached locally for viewing while offline.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-offline.h>
+ *
+ *   ESourceOffline *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_OFFLINE);
+ * ]|
+ **/
+
+#include "e-source-offline.h"
+
+#define E_SOURCE_OFFLINE_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_OFFLINE, ESourceOfflinePrivate))
+
+struct _ESourceOfflinePrivate {
+	gboolean stay_synchronized;
+};
+
+enum {
+	PROP_0,
+	PROP_STAY_SYNCHRONIZED
+};
+
+G_DEFINE_TYPE (
+	ESourceOffline,
+	e_source_offline,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_offline_set_property (GObject *object,
+                             guint property_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_STAY_SYNCHRONIZED:
+			e_source_offline_set_stay_synchronized (
+				E_SOURCE_OFFLINE (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_offline_get_property (GObject *object,
+                             guint property_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_STAY_SYNCHRONIZED:
+			g_value_set_boolean (
+				value,
+				e_source_offline_get_stay_synchronized (
+				E_SOURCE_OFFLINE (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_source_offline_class_init (ESourceOfflineClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceOfflinePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_offline_set_property;
+	object_class->get_property = source_offline_get_property;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_OFFLINE;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_STAY_SYNCHRONIZED,
+		g_param_spec_boolean (
+			"stay-synchronized",
+			"StaySynchronized",
+			"Keep remote content synchronized locally",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_offline_init (ESourceOffline *extension)
+{
+	extension->priv = E_SOURCE_OFFLINE_GET_PRIVATE (extension);
+}
+
+/**
+ * e_source_offline_get_stay_synchronized:
+ * @extension: an #ESourceOffline
+ *
+ * Returns whether data from a remote server should be cached locally
+ * for viewing while offline.  Backends are responsible for implementing
+ * such caching.
+ *
+ * Returns: whether data should be cached for offline
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_offline_get_stay_synchronized (ESourceOffline *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_OFFLINE (extension), FALSE);
+
+	return extension->priv->stay_synchronized;
+}
+
+/**
+ * e_source_offline_set_stay_synchronized:
+ * @extension: an #ESourceOffline
+ * @stay_synchronized: whether data should be cached for offline
+ *
+ * Sets whether data from a remote server should be cached locally for
+ * viewing while offline.  Backends are responsible for implementing
+ * such caching.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_offline_set_stay_synchronized (ESourceOffline *extension,
+                                        gboolean stay_synchronized)
+{
+	g_return_if_fail (E_IS_SOURCE_OFFLINE (extension));
+
+	extension->priv->stay_synchronized = stay_synchronized;
+
+	g_object_notify (G_OBJECT (extension), "stay-synchronized");
+}
diff --git a/libedataserver/e-source-offline.h b/libedataserver/e-source-offline.h
new file mode 100644
index 0000000..00053db
--- /dev/null
+++ b/libedataserver/e-source-offline.h
@@ -0,0 +1,85 @@
+/*
+ * e-source-offline.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_SOURCE_OFFLINE_H
+#define E_SOURCE_OFFLINE_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_OFFLINE \
+	(e_source_offline_get_type ())
+#define E_SOURCE_OFFLINE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_OFFLINE, ESourceOffline))
+#define E_SOURCE_OFFLINE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_OFFLINE, ESourceOfflineClass))
+#define E_IS_SOURCE_OFFLINE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_OFFLINE))
+#define E_IS_SOURCE_OFFLINE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_OFFLINE))
+#define E_SOURCE_OFFLINE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_OFFLINE, ESourceOfflineClass))
+
+/**
+ * E_SOURCE_EXTENSION_OFFLINE:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceOffline.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_OFFLINE "Offline"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceOffline ESourceOffline;
+typedef struct _ESourceOfflineClass ESourceOfflineClass;
+typedef struct _ESourceOfflinePrivate ESourceOfflinePrivate;
+
+/**
+ * ESourceOffline:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceOffline {
+	ESourceExtension parent;
+	ESourceOfflinePrivate *priv;
+};
+
+struct _ESourceOfflineClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_offline_get_type	(void) G_GNUC_CONST;
+gboolean	e_source_offline_get_stay_synchronized
+						(ESourceOffline *extension);
+void		e_source_offline_set_stay_synchronized
+						(ESourceOffline *extension,
+						 gboolean stay_synchronized);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_OFFLINE_H */
diff --git a/libedataserver/e-source-openpgp.c b/libedataserver/e-source-openpgp.c
new file mode 100644
index 0000000..a6e7aeb
--- /dev/null
+++ b/libedataserver/e-source-openpgp.c
@@ -0,0 +1,561 @@
+/*
+ * e-source-openpgp.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-openpgp
+ * @include: libedataserver/e-source-openpgp.h
+ * @short_description: #ESource extension for OpenPGP settings
+ *
+ * The #ESourceOpenPGP extension tracks OpenPGP (RFC 4880) settings to be
+ * applied to outgoing mail messages.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-openpgp.h>
+ *
+ *   ESourceOpenPGP *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_OPENPGP);
+ * ]|
+ **/
+
+#include "e-source-openpgp.h"
+
+#define E_SOURCE_OPENPGP_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGPPrivate))
+
+struct _ESourceOpenPGPPrivate {
+	GMutex *property_lock;
+	gchar *key_id;
+	gchar *signing_algorithm;
+
+	gboolean always_trust;
+	gboolean encrypt_to_self;
+	gboolean sign_by_default;
+};
+
+enum {
+	PROP_0,
+	PROP_ALWAYS_TRUST,
+	PROP_ENCRYPT_TO_SELF,
+	PROP_KEY_ID,
+	PROP_SIGNING_ALGORITHM,
+	PROP_SIGN_BY_DEFAULT
+};
+
+G_DEFINE_TYPE (
+	ESourceOpenPGP,
+	e_source_openpgp,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_openpgp_set_property (GObject *object,
+                             guint property_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ALWAYS_TRUST:
+			e_source_openpgp_set_always_trust (
+				E_SOURCE_OPENPGP (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_ENCRYPT_TO_SELF:
+			e_source_openpgp_set_encrypt_to_self (
+				E_SOURCE_OPENPGP (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_KEY_ID:
+			e_source_openpgp_set_key_id (
+				E_SOURCE_OPENPGP (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SIGNING_ALGORITHM:
+			e_source_openpgp_set_signing_algorithm (
+				E_SOURCE_OPENPGP (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SIGN_BY_DEFAULT:
+			e_source_openpgp_set_sign_by_default (
+				E_SOURCE_OPENPGP (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_openpgp_get_property (GObject *object,
+                             guint property_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ALWAYS_TRUST:
+			g_value_set_boolean (
+				value,
+				e_source_openpgp_get_always_trust (
+				E_SOURCE_OPENPGP (object)));
+			return;
+
+		case PROP_ENCRYPT_TO_SELF:
+			g_value_set_boolean (
+				value,
+				e_source_openpgp_get_encrypt_to_self (
+				E_SOURCE_OPENPGP (object)));
+			return;
+
+		case PROP_KEY_ID:
+			g_value_take_string (
+				value,
+				e_source_openpgp_dup_key_id (
+				E_SOURCE_OPENPGP (object)));
+			return;
+
+		case PROP_SIGNING_ALGORITHM:
+			g_value_take_string (
+				value,
+				e_source_openpgp_dup_signing_algorithm (
+				E_SOURCE_OPENPGP (object)));
+			return;
+
+		case PROP_SIGN_BY_DEFAULT:
+			g_value_set_boolean (
+				value,
+				e_source_openpgp_get_sign_by_default (
+				E_SOURCE_OPENPGP (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_openpgp_finalize (GObject *object)
+{
+	ESourceOpenPGPPrivate *priv;
+
+	priv = E_SOURCE_OPENPGP_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->key_id);
+	g_free (priv->signing_algorithm);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_openpgp_parent_class)->finalize (object);
+}
+
+static void
+e_source_openpgp_class_init (ESourceOpenPGPClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceOpenPGPPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_openpgp_set_property;
+	object_class->get_property = source_openpgp_get_property;
+	object_class->finalize = source_openpgp_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_OPENPGP;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ALWAYS_TRUST,
+		g_param_spec_boolean (
+			"always-trust",
+			"Always Trust",
+			"Always trust keys in my keyring",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ENCRYPT_TO_SELF,
+		g_param_spec_boolean (
+			"encrypt-to-self",
+			"Encrypt To Self",
+			"Always encrypt to myself",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_KEY_ID,
+		g_param_spec_string (
+			"key-id",
+			"Key ID",
+			"PGP/GPG Key ID",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGNING_ALGORITHM,
+		g_param_spec_string (
+			"signing-algorithm",
+			"Signing Algorithm",
+			"Hash algorithm used to sign messages",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGN_BY_DEFAULT,
+		g_param_spec_boolean (
+			"sign-by-default",
+			"Sign By Default",
+			"Sign outgoing messages by default",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_openpgp_init (ESourceOpenPGP *extension)
+{
+	extension->priv = E_SOURCE_OPENPGP_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_openpgp_get_always_trust:
+ * @extension: an #ESourceOpenPGP
+ *
+ * Returns whether to skip key validation and assume that used keys are
+ * always fully trusted.
+ *
+ * Returns: whether used keys are always fully trusted
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_openpgp_get_always_trust (ESourceOpenPGP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), FALSE);
+
+	return extension->priv->always_trust;
+}
+
+/**
+ * e_source_openpgp_set_always_trust:
+ * @extension: an #ESourceOpenPGP
+ * @always_trust: whether used keys are always fully trusted
+ *
+ * Sets whether to skip key validation and assume that used keys are
+ * always fully trusted.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_openpgp_set_always_trust (ESourceOpenPGP *extension,
+                                   gboolean always_trust)
+{
+	g_return_if_fail (E_IS_SOURCE_OPENPGP (extension));
+
+	extension->priv->always_trust = always_trust;
+
+	g_object_notify (G_OBJECT (extension), "always-trust");
+}
+
+/**
+ * e_source_openpgp_get_encrypt_to_self:
+ * @extension: an #ESourceOpenPGP
+ *
+ * Returns whether to "encrypt-to-self" when sending encrypted messages.
+ *
+ * Returns: whether to "encrypt-to-self"
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_openpgp_get_encrypt_to_self (ESourceOpenPGP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), FALSE);
+
+	return extension->priv->encrypt_to_self;
+}
+
+/**
+ * e_source_openpgp_set_encrypt_to_self:
+ * @extension: an #ESourceOpenPGP
+ * @encrypt_to_self: whether to "encrypt-to-self"
+ *
+ * Sets whether to "encrypt-to-self" when sending encrypted messages.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_openpgp_set_encrypt_to_self (ESourceOpenPGP *extension,
+                                      gboolean encrypt_to_self)
+{
+	g_return_if_fail (E_IS_SOURCE_OPENPGP (extension));
+
+	extension->priv->encrypt_to_self = encrypt_to_self;
+
+	g_object_notify (G_OBJECT (extension), "encrypt-to-self");
+}
+
+/**
+ * e_source_openpgp_get_key_id:
+ * @extension: an #ESourceOpenPGP
+ *
+ * Returns the OpenPGP key ID used to sign and encrypt messages.
+ *
+ * Returns: the key ID used to sign and encrypt messages
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_openpgp_get_key_id (ESourceOpenPGP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL);
+
+	return extension->priv->key_id;
+}
+
+/**
+ * e_source_openpgp_dup_key_id:
+ * @extension: an #ESourceOpenPGP
+ *
+ * Thread-safe variation of e_source_openpgp_get_key_id().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceOpenPGP:key-id
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_openpgp_dup_key_id (ESourceOpenPGP *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_openpgp_get_key_id (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_openpgp_set_key_id:
+ * @extension: an #ESourceOpenPGP
+ * @key_id: the key ID used to sign and encrypt messages
+ *
+ * Sets the OpenPGP key ID used to sign and encrypt messages.
+ *
+ * The internal copy of @key_id is automatically stripped of leading and
+ * trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_openpgp_set_key_id (ESourceOpenPGP *extension,
+                             const gchar *key_id)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_OPENPGP (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (key_id);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->key_id);
+	extension->priv->key_id = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "key-id");
+}
+
+/**
+ * e_source_openpgp_get_signing_algorithm:
+ * @extension: an #ESourceOpenPGP
+ *
+ * Returns the name of the hash algorithm used to digitally sign outgoing
+ * messages.
+ *
+ * Returns: the signing algorithm for outgoing messages
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_openpgp_get_signing_algorithm (ESourceOpenPGP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL);
+
+	return extension->priv->signing_algorithm;
+}
+
+/**
+ * e_source_openpgp_dup_signing_algorithm:
+ * @extension: an #ESourceOpenPGP
+ *
+ * Thread-safe variation of e_source_openpgp_get_signing_algorithm().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceOpenPGP:signing-algorithm
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_openpgp_dup_signing_algorithm (ESourceOpenPGP *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_openpgp_get_signing_algorithm (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_openpgp_set_signing_algorithm:
+ * @extension: an #ESourceOpenPGP
+ * @signing_algorithm: the signing algorithm for outgoing messages
+ *
+ * Sets the name of the hash algorithm used to digitally sign outgoing
+ * messages.
+ *
+ * The internal copy of @signing_algorithm is automatically stripped of
+ * leading and trailing whitespace.  If the resulting string is empty,
+ * %NULL is set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_openpgp_set_signing_algorithm (ESourceOpenPGP *extension,
+                                        const gchar *signing_algorithm)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_OPENPGP (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (signing_algorithm);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->signing_algorithm);
+	extension->priv->signing_algorithm = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "signing-algorithm");
+}
+
+/**
+ * e_source_openpgp_get_sign_by_default:
+ * @extension: an #ESourceOpenPGP
+ *
+ * Returns whether to digitally sign outgoing messages by default using
+ * OpenPGP-compliant software such as GNU Privacy Guard (GnuPG).
+ *
+ * Returns: whether to sign outgoing messages by default
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_openpgp_get_sign_by_default (ESourceOpenPGP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), FALSE);
+
+	return extension->priv->sign_by_default;
+}
+
+/**
+ * e_source_openpgp_set_sign_by_default:
+ * @extension: an #ESourceOpenPGP
+ * @sign_by_default: whether to sign outgoing messages by default
+ *
+ * Sets whether to digitally sign outgoing messages by default using
+ * OpenPGP-compliant software such as GNU Privacy Guard (GnuPG).
+ *
+ * Since: 3.6
+ **/
+void
+e_source_openpgp_set_sign_by_default (ESourceOpenPGP *extension,
+                                      gboolean sign_by_default)
+{
+	g_return_if_fail (E_IS_SOURCE_OPENPGP (extension));
+
+	extension->priv->sign_by_default = sign_by_default;
+
+	g_object_notify (G_OBJECT (extension), "sign-by-default");
+}
+
diff --git a/libedataserver/e-source-openpgp.h b/libedataserver/e-source-openpgp.h
new file mode 100644
index 0000000..840b929
--- /dev/null
+++ b/libedataserver/e-source-openpgp.h
@@ -0,0 +1,107 @@
+/*
+ * e-source-openpgp.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_SOURCE_OPENPGP_H
+#define E_SOURCE_OPENPGP_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_OPENPGP \
+	(e_source_openpgp_get_type ())
+#define E_SOURCE_OPENPGP(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGP))
+#define E_SOURCE_OPENPGP_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGPClass))
+#define E_IS_SOURCE_OPENPGP(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_OPENPGP))
+#define E_IS_SOURCE_OPENPGP_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_OPENPGP))
+#define E_SOURCE_OPENPGP_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGPClass))
+
+/**
+ * E_SOURCE_EXTENSION_OPENPGP:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceOpenPGP.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_OPENPGP "Pretty Good Privacy (OpenPGP)"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceOpenPGP ESourceOpenPGP;
+typedef struct _ESourceOpenPGPClass ESourceOpenPGPClass;
+typedef struct _ESourceOpenPGPPrivate ESourceOpenPGPPrivate;
+
+/**
+ * ESourceOpenPGP:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceOpenPGP {
+	ESourceExtension parent;
+	ESourceOpenPGPPrivate *priv;
+};
+
+struct _ESourceOpenPGPClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_openpgp_get_type	(void) G_GNUC_CONST;
+gboolean	e_source_openpgp_get_always_trust
+						(ESourceOpenPGP *extension);
+void		e_source_openpgp_set_always_trust
+						(ESourceOpenPGP *extension,
+						 gboolean always_trust);
+gboolean	e_source_openpgp_get_encrypt_to_self
+						(ESourceOpenPGP *extension);
+void		e_source_openpgp_set_encrypt_to_self
+						(ESourceOpenPGP *extension,
+						 gboolean encrypt_to_self);
+const gchar *	e_source_openpgp_get_key_id	(ESourceOpenPGP *extension);
+gchar *		e_source_openpgp_dup_key_id	(ESourceOpenPGP *extension);
+void		e_source_openpgp_set_key_id	(ESourceOpenPGP *extension,
+						 const gchar *key_id);
+const gchar *	e_source_openpgp_get_signing_algorithm
+						(ESourceOpenPGP *extension);
+gchar *		e_source_openpgp_dup_signing_algorithm
+						(ESourceOpenPGP *extension);
+void		e_source_openpgp_set_signing_algorithm
+						(ESourceOpenPGP *extension,
+						 const gchar *signing_algorithm);
+gboolean	e_source_openpgp_get_sign_by_default
+						(ESourceOpenPGP *extension);
+void		e_source_openpgp_set_sign_by_default
+						(ESourceOpenPGP *extension,
+						 gboolean sign_by_default);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_OPENPGP_H */
+
diff --git a/libedataserver/e-source-refresh.c b/libedataserver/e-source-refresh.c
new file mode 100644
index 0000000..9a2c568
--- /dev/null
+++ b/libedataserver/e-source-refresh.c
@@ -0,0 +1,629 @@
+/*
+ * e-source-refresh.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-refresh
+ * @include: libedataserver/e-source-refresh.h
+ * @short_description: #ESource extension for refresh settings
+ *
+ * The #ESourceRefresh extension tracks the interval for fetching
+ * updates from a remote server.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-refresh.h>
+ *
+ *   ESourceRefresh *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_REFRESH);
+ * ]|
+ **/
+
+#include "e-source-refresh.h"
+
+#define E_SOURCE_REFRESH_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_REFRESH, ESourceRefreshPrivate))
+
+typedef struct _TimeoutNode TimeoutNode;
+
+struct _ESourceRefreshPrivate {
+	gboolean enabled;
+	guint interval_minutes;
+
+	GMutex *timeout_lock;
+	GHashTable *timeout_table;
+	guint next_timeout_id;
+};
+
+struct _TimeoutNode {
+	GSource *source;
+	GMainContext *context;
+	ESourceRefresh *extension;  /* not referenced */
+
+	ESourceRefreshFunc callback;
+	gpointer user_data;
+	GDestroyNotify notify;
+};
+
+enum {
+	PROP_0,
+	PROP_ENABLED,
+	PROP_INTERVAL_MINUTES
+};
+
+G_DEFINE_TYPE (
+	ESourceRefresh,
+	e_source_refresh,
+	E_TYPE_SOURCE_EXTENSION)
+
+static TimeoutNode *
+timeout_node_new (ESourceRefresh *extension,
+                  GMainContext *context,
+                  ESourceRefreshFunc callback,
+                  gpointer user_data,
+                  GDestroyNotify notify)
+{
+	TimeoutNode *node;
+
+	if (context != NULL)
+		g_main_context_ref (context);
+
+	node = g_slice_new0 (TimeoutNode);
+	node->context = context;
+	node->callback = callback;
+	node->user_data = user_data;
+	node->notify = notify;
+
+	/* Do not reference.  The timeout node will
+	 * not outlive the ESourceRefresh extension. */
+	node->extension = extension;
+
+	return node;
+}
+
+static gboolean
+timeout_node_invoke (gpointer data)
+{
+	TimeoutNode *node = data;
+	ESourceExtension *extension;
+	ESource *source;
+
+	extension = E_SOURCE_EXTENSION (node->extension);
+	source = e_source_extension_get_source (extension);
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+	/* We allow timeouts to be scheduled for disabled data sources
+	 * but we don't invoke the callback.  Keeps the logic simple. */
+	if (e_source_get_enabled (source))
+		node->callback (source, node->user_data);
+
+	return TRUE;
+}
+
+static void
+timeout_node_attach (TimeoutNode *node)
+{
+	guint interval_minutes;
+
+	if (node->source != NULL)
+		return;
+
+	interval_minutes =
+		e_source_refresh_get_interval_minutes (node->extension);
+	node->source = g_timeout_source_new_seconds (interval_minutes * 60);
+
+	g_source_set_callback (
+		node->source,
+		timeout_node_invoke,
+		node,
+		(GDestroyNotify) NULL);
+
+	g_source_attach (node->source, node->context);
+}
+
+static void
+timeout_node_detach (TimeoutNode *node)
+{
+	if (node->source == NULL)
+		return;
+
+	g_source_destroy (node->source);
+	g_source_unref (node->source);
+	node->source = NULL;
+}
+
+static void
+timeout_node_free (TimeoutNode *node)
+{
+	if (node->source != NULL)
+		timeout_node_detach (node);
+
+	if (node->context != NULL)
+		g_main_context_unref (node->context);
+
+	if (node->notify != NULL)
+		node->notify (node->user_data);
+
+	g_slice_free (TimeoutNode, node);
+}
+
+static void
+source_refresh_update_timeouts (ESourceRefresh *extension,
+                                gboolean invoke_callbacks)
+{
+	GList *list, *link;
+
+	g_mutex_lock (extension->priv->timeout_lock);
+
+	list = g_hash_table_get_values (extension->priv->timeout_table);
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		TimeoutNode *node = link->data;
+
+		timeout_node_detach (node);
+
+		if (invoke_callbacks)
+			timeout_node_invoke (node);
+
+		if (e_source_refresh_get_enabled (extension))
+			timeout_node_attach (node);
+	}
+
+	g_list_free (list);
+
+	g_mutex_unlock (extension->priv->timeout_lock);
+}
+
+static void
+source_refresh_notify_enabled_cb (ESource *source,
+                                  GParamSpec *pspec,
+                                  ESourceRefresh *extension)
+{
+	if (e_source_get_enabled (source))
+		e_source_refresh_force_timeout (source);
+}
+
+static void
+source_refresh_set_property (GObject *object,
+                             guint property_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ENABLED:
+			e_source_refresh_set_enabled (
+				E_SOURCE_REFRESH (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_INTERVAL_MINUTES:
+			e_source_refresh_set_interval_minutes (
+				E_SOURCE_REFRESH (object),
+				g_value_get_uint (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_refresh_get_property (GObject *object,
+                             guint property_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ENABLED:
+			g_value_set_boolean (
+				value,
+				e_source_refresh_get_enabled (
+				E_SOURCE_REFRESH (object)));
+			return;
+
+		case PROP_INTERVAL_MINUTES:
+			g_value_set_uint (
+				value,
+				e_source_refresh_get_interval_minutes (
+				E_SOURCE_REFRESH (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_refresh_dispose (GObject *object)
+{
+	ESourceRefreshPrivate *priv;
+
+	priv = E_SOURCE_REFRESH_GET_PRIVATE (object);
+
+	g_hash_table_remove_all (priv->timeout_table);
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_source_refresh_parent_class)->dispose (object);
+}
+
+static void
+source_refresh_finalize (GObject *object)
+{
+	ESourceRefreshPrivate *priv;
+
+	priv = E_SOURCE_REFRESH_GET_PRIVATE (object);
+
+	g_mutex_free (priv->timeout_lock);
+	g_hash_table_destroy (priv->timeout_table);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_refresh_parent_class)->finalize (object);
+}
+
+static void
+source_refresh_constructed (GObject *object)
+{
+	ESourceExtension *extension;
+	ESource *source;
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_source_refresh_parent_class)->constructed (object);
+
+	extension = E_SOURCE_EXTENSION (object);
+	source = e_source_extension_get_source (extension);
+
+	/* There should be no lifecycle issues here
+	 * since we get finalized with our ESource. */
+	g_signal_connect (
+		source, "notify::enabled",
+		G_CALLBACK (source_refresh_notify_enabled_cb),
+		extension);
+}
+
+static void
+e_source_refresh_class_init (ESourceRefreshClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceRefreshPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_refresh_set_property;
+	object_class->get_property = source_refresh_get_property;
+	object_class->dispose = source_refresh_dispose;
+	object_class->finalize = source_refresh_finalize;
+	object_class->constructed = source_refresh_constructed;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_REFRESH;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ENABLED,
+		g_param_spec_boolean (
+			"enabled",
+			"Enabled",
+			"Whether to periodically refresh",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_INTERVAL_MINUTES,
+		g_param_spec_uint (
+			"interval-minutes",
+			"Interval in Minutes",
+			"Refresh interval in minutes",
+			0, G_MAXUINT, 60,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_refresh_init (ESourceRefresh *extension)
+{
+	GHashTable *timeout_table;
+
+	timeout_table = g_hash_table_new_full (
+		(GHashFunc) g_direct_hash,
+		(GEqualFunc) g_direct_equal,
+		(GDestroyNotify) NULL,
+		(GDestroyNotify) timeout_node_free);
+
+	extension->priv = E_SOURCE_REFRESH_GET_PRIVATE (extension);
+	extension->priv->timeout_lock = g_mutex_new ();
+	extension->priv->timeout_table = timeout_table;
+	extension->priv->next_timeout_id = 1;
+}
+
+/**
+ * e_source_refresh_get_enabled:
+ * @extension: an #ESourceRefresh
+ *
+ * Returns whether to periodically fetch updates from a remote server.
+ *
+ * The refresh interval is determined by the #ESourceRefresh:interval-minutes
+ * property.
+ *
+ * Returns: whether periodic refresh is enabled
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_refresh_get_enabled (ESourceRefresh *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REFRESH (extension), FALSE);
+
+	return extension->priv->enabled;
+}
+
+/**
+ * e_source_refresh_set_enabled:
+ * @extension: an #ESourceRefresh
+ * @enabled: whether to enable periodic refresh
+ *
+ * Sets whether to periodically fetch updates from a remote server.
+ *
+ * The refresh interval is determined by the #ESourceRefresh:interval-minutes
+ * property.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_refresh_set_enabled (ESourceRefresh *extension,
+                              gboolean enabled)
+{
+	g_return_if_fail (E_IS_SOURCE_REFRESH (extension));
+
+	if (enabled == extension->priv->enabled)
+		return;
+
+	extension->priv->enabled = enabled;
+
+	g_object_notify (G_OBJECT (extension), "enabled");
+
+	source_refresh_update_timeouts (extension, FALSE);
+}
+
+/**
+ * e_source_refresh_get_interval_minutes:
+ * @extension: an #ESourceRefresh
+ *
+ * Returns the interval for fetching updates from a remote server.
+ *
+ * Note this value is only effective when the #ESourceRefresh:enabled
+ * property is %TRUE.
+ *
+ * Returns: the interval in minutes
+ *
+ * Since: 3.6
+ **/
+guint
+e_source_refresh_get_interval_minutes (ESourceRefresh *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REFRESH (extension), FALSE);
+
+	return extension->priv->interval_minutes;
+}
+
+/**
+ * e_source_refresh_set_interval_minutes:
+ * @extension: an #ESourceRefresh
+ * @interval_minutes: the interval in minutes
+ *
+ * Sets the interval for fetching updates from a remote server.
+ *
+ * Note this value is only effective when the #ESourceRefresh:enabled
+ * property is %TRUE.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_refresh_set_interval_minutes (ESourceRefresh *extension,
+                                       guint interval_minutes)
+{
+	g_return_if_fail (E_IS_SOURCE_REFRESH (extension));
+
+	if (interval_minutes == extension->priv->interval_minutes)
+		return;
+
+	extension->priv->interval_minutes = interval_minutes;
+
+	g_object_notify (G_OBJECT (extension), "interval-minutes");
+
+	source_refresh_update_timeouts (extension, FALSE);
+}
+
+/**
+ * e_source_refresh_add_timeout:
+ * @source: an #ESource
+ * @context: a #GMainContext (if %NULL, the default context will be used)
+ * @callback: function to call on each timeout
+ * @user_data: data to pass to @callback
+ * @notify: function to call when the timeout is removed, or %NULL
+ *
+ * This is a simple way to schedule a periodic data source refresh.
+ *
+ * Adds a timeout #GSource to @context and handles all the bookkeeping
+ * if @source's refresh #ESourceRefresh:enabled state or its refresh
+ * #ESourceRefresh:interval-minutes value changes.  The @callback is
+ * expected to dispatch an asynchronous job to connect to and fetch
+ * updates from a remote server.
+ *
+ * The returned ID can be passed to e_source_refresh_remove_timeout() to
+ * remove the timeout from @context.  Note the ID is a private handle and
+ * cannot be passed to g_source_remove().
+ *
+ * Returns: a refresh timeout ID
+ *
+ * Since: 3.6
+ **/
+guint
+e_source_refresh_add_timeout (ESource *source,
+                              GMainContext *context,
+                              ESourceRefreshFunc callback,
+                              gpointer user_data,
+                              GDestroyNotify notify)
+{
+	ESourceRefresh *extension;
+	const gchar *extension_name;
+	TimeoutNode *node;
+	guint timeout_id;
+	gpointer key;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), 0);
+	g_return_val_if_fail (callback != NULL, 0);
+
+	extension_name = E_SOURCE_EXTENSION_REFRESH;
+	extension = e_source_get_extension (source, extension_name);
+
+	g_mutex_lock (extension->priv->timeout_lock);
+
+	timeout_id = extension->priv->next_timeout_id++;
+
+	key = GUINT_TO_POINTER (timeout_id);
+	node = timeout_node_new (
+		extension, context, callback, user_data, notify);
+	g_hash_table_insert (extension->priv->timeout_table, key, node);
+
+	if (e_source_refresh_get_enabled (extension))
+		timeout_node_attach (node);
+
+	g_mutex_unlock (extension->priv->timeout_lock);
+
+	return timeout_id;
+}
+
+/**
+ * e_source_refresh_force_timeout:
+ * @source: an #ESource
+ *
+ * For all timeouts added with e_source_refresh_add_timeout(), invokes
+ * the #ESourceRefreshFunc callback immediately and then, if the refresh
+ * #ESourceRefresh:enabled state is TRUE, reschedules the timeout.
+ *
+ * This function is called automatically when the #ESource switches from
+ * disabled to enabled, but can also be useful when a network connection
+ * becomes available or when waking up from hibernation or suspend.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_refresh_force_timeout (ESource *source)
+{
+	ESourceRefresh *extension;
+	const gchar *extension_name;
+
+	g_return_if_fail (E_IS_SOURCE (source));
+
+	extension_name = E_SOURCE_EXTENSION_REFRESH;
+	extension = e_source_get_extension (source, extension_name);
+
+	source_refresh_update_timeouts (extension, TRUE);
+}
+
+/**
+ * e_source_refresh_remove_timeout:
+ * @source: an #ESource
+ * @refresh_timeout_id: a refresh timeout ID
+ *
+ * Removes a timeout #GSource added by e_source_refresh_add_timeout().
+ *
+ * Returns: %TRUE if the timeout was found and removed
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_refresh_remove_timeout (ESource *source,
+                                 guint refresh_timeout_id)
+{
+	ESourceRefresh *extension;
+	const gchar *extension_name;
+	gboolean removed;
+	gpointer key;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (refresh_timeout_id > 0, FALSE);
+
+	extension_name = E_SOURCE_EXTENSION_REFRESH;
+	extension = e_source_get_extension (source, extension_name);
+
+	g_mutex_lock (extension->priv->timeout_lock);
+
+	key = GUINT_TO_POINTER (refresh_timeout_id);
+	removed = g_hash_table_remove (extension->priv->timeout_table, key);
+
+	g_mutex_unlock (extension->priv->timeout_lock);
+
+	return removed;
+}
+
+/**
+ * e_source_refresh_remove_timeouts_by_data:
+ * @source: an #ESource
+ * @user_data: user data to match against timeout callbacks
+ *
+ * Removes all timeout #GSource's added by e_source_refresh_add_timeout()
+ * whose callback data pointer matches @user_data.
+ *
+ * Returns: the number of timeouts found and removed
+ *
+ * Since: 3.6
+ **/
+guint
+e_source_refresh_remove_timeouts_by_data (ESource *source,
+                                          gpointer user_data)
+{
+	ESourceRefresh *extension;
+	const gchar *extension_name;
+	GQueue trash = G_QUEUE_INIT;
+	GHashTableIter iter;
+	gpointer key, value;
+	guint n_removed = 0;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), 0);
+
+	extension_name = E_SOURCE_EXTENSION_REFRESH;
+	extension = e_source_get_extension (source, extension_name);
+
+	g_mutex_lock (extension->priv->timeout_lock);
+
+	g_hash_table_iter_init (&iter, extension->priv->timeout_table);
+
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		TimeoutNode *node = value;
+
+		if (node->user_data == user_data)
+			g_queue_push_tail (&trash, key);
+	}
+
+	while ((key = g_queue_pop_head (&trash)) != NULL)
+		if (g_hash_table_remove (extension->priv->timeout_table, key))
+			n_removed++;
+
+	g_mutex_unlock (extension->priv->timeout_lock);
+
+	return n_removed;
+}
+
diff --git a/libedataserver/e-source-refresh.h b/libedataserver/e-source-refresh.h
new file mode 100644
index 0000000..9b05ba9
--- /dev/null
+++ b/libedataserver/e-source-refresh.h
@@ -0,0 +1,103 @@
+/*
+ * e-source-refresh.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_SOURCE_REFRESH_H
+#define E_SOURCE_REFRESH_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_REFRESH \
+	(e_source_refresh_get_type ())
+#define E_SOURCE_REFRESH(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_REFRESH, ESourceRefresh))
+#define E_SOURCE_REFRESH_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_REFRESH, ESourceRefreshClass))
+#define E_IS_SOURCE_REFRESH(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_REFRESH))
+#define E_IS_SOURCE_REFRESH_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_REFRESH))
+#define E_SOURCE_REFRESH_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_REFRESH, ESourceRefreshClass))
+
+/**
+ * E_SOURCE_EXTENSION_REFRESH:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceRefresh.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_REFRESH "Refresh"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceRefresh ESourceRefresh;
+typedef struct _ESourceRefreshClass ESourceRefreshClass;
+typedef struct _ESourceRefreshPrivate ESourceRefreshPrivate;
+
+/**
+ * ESourceRefresh:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceRefresh {
+	ESourceExtension parent;
+	ESourceRefreshPrivate *priv;
+};
+
+struct _ESourceRefreshClass {
+	ESourceExtensionClass parent_class;
+};
+
+typedef void	(*ESourceRefreshFunc)		(ESource *source,
+						 gpointer user_data);
+
+GType		e_source_refresh_get_type	(void) G_GNUC_CONST;
+gboolean	e_source_refresh_get_enabled	(ESourceRefresh *extension);
+void		e_source_refresh_set_enabled	(ESourceRefresh *extension,
+						 gboolean enabled);
+guint		e_source_refresh_get_interval_minutes
+						(ESourceRefresh *extension);
+void		e_source_refresh_set_interval_minutes
+						(ESourceRefresh *extension,
+						 guint interval_minutes);
+
+guint		e_source_refresh_add_timeout	(ESource *source,
+						 GMainContext *context,
+						 ESourceRefreshFunc callback,
+						 gpointer user_data,
+						 GDestroyNotify notify);
+void		e_source_refresh_force_timeout	(ESource *source);
+gboolean	e_source_refresh_remove_timeout	(ESource *source,
+						 guint refresh_timeout_id);
+guint		e_source_refresh_remove_timeouts_by_data
+						(ESource *source,
+						 gpointer user_data);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_REFRESH_H */
diff --git a/libedataserver/e-source-registry.c b/libedataserver/e-source-registry.c
new file mode 100644
index 0000000..f8d9109
--- /dev/null
+++ b/libedataserver/e-source-registry.c
@@ -0,0 +1,3561 @@
+/*
+ * 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/>
+ *
+ */
+
+/**
+ * SECTION: e-source-registry
+ * @include: libedataserver/e-source-registry.h
+ * @short_description: A central repository for data sources
+ *
+ * The #ESourceRegistry is a global singleton store for all #ESource
+ * instances.  It uses file monitors to react to key file creation and
+ * deletion events, either constructing an #ESource instance from the
+ * newly created key file, or removing from the logical #ESource
+ * hierarchy the instance corresponding to the deleted key file.
+ *
+ * The #ESourceRegistry can be queried for individual #ESource instances
+ * by their unique identifier string or key file path, for collections of
+ * #ESource instances having a particular extension, or for all available
+ * #ESource instances.
+ *
+ * The #ESourceRegistry API also provides a front-end for the
+ * "org.gnome.Evolution.DefaultSources" #GSettings schema which tracks
+ * which #ESource instances are designated to be the user's default address
+ * book, calendar, memo list and task list for desktop integration.
+ **/
+
+#include "e-source-registry.h"
+
+#include <config.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+
+/* XXX Yeah, yeah... */
+#define GCR_API_SUBJECT_TO_CHANGE
+
+#include <gcr/gcr-base.h>
+
+/* Private D-Bus classes. */
+#include <e-dbus-source.h>
+#include <e-dbus-source-manager.h>
+
+#include <libedataserver/e-marshal.h>
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-source-collection.h>
+
+/* Needed for the defaults API. */
+#include <libedataserver/e-source-address-book.h>
+#include <libedataserver/e-source-calendar.h>
+#include <libedataserver/e-source-mail-account.h>
+#include <libedataserver/e-source-mail-identity.h>
+
+#include "e-dbus-authenticator.h"
+
+#define E_SOURCE_REGISTRY_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_REGISTRY, ESourceRegistryPrivate))
+
+#define DBUS_OBJECT_PATH "/org/gnome/evolution/dataserver/SourceManager"
+#define GSETTINGS_SCHEMA "org.gnome.Evolution.DefaultSources"
+
+/* Built-in data source UIDs. */
+#define E_SOURCE_BUILTIN_ADDRESS_BOOK_UID	"system-address-book"
+#define E_SOURCE_BUILTIN_CALENDAR_UID		"system-calendar"
+#define E_SOURCE_BUILTIN_MAIL_ACCOUNT_UID 	"local"
+#define E_SOURCE_BUILTIN_MEMO_LIST_UID		"system-memo-list"
+#define E_SOURCE_BUILTIN_TASK_LIST_UID		"system-task-list"
+
+/* GSettings keys for default data sources. */
+#define E_SETTINGS_DEFAULT_ADDRESS_BOOK_KEY	"default-address-book"
+#define E_SETTINGS_DEFAULT_CALENDAR_KEY		"default-calendar"
+#define E_SETTINGS_DEFAULT_MAIL_ACCOUNT_KEY	"default-mail-account"
+#define E_SETTINGS_DEFAULT_MAIL_IDENTITY_KEY	"default-mail-identity"
+#define E_SETTINGS_DEFAULT_MEMO_LIST_KEY	"default-memo-list"
+#define E_SETTINGS_DEFAULT_TASK_LIST_KEY	"default-task-list"
+
+typedef struct _AsyncContext AsyncContext;
+typedef struct _AuthContext AuthContext;
+typedef struct _CreateContext CreateContext;
+typedef struct _SourceClosure SourceClosure;
+typedef struct _ThreadClosure ThreadClosure;
+
+struct _ESourceRegistryPrivate {
+	GMainContext *main_context;
+
+	GThread *manager_thread;
+	ThreadClosure *thread_closure;
+
+	GDBusObjectManager *dbus_object_manager;
+	EDBusSourceManager *dbus_source_manager;
+
+	GHashTable *object_path_table;
+	GMutex *object_path_table_lock;
+
+	GHashTable *sources;
+	GMutex *sources_lock;
+
+	GSettings *settings;
+};
+
+struct _AsyncContext {
+	ESource *input_source;
+	ESource *output_source;
+	ESourceAuthenticator *auth;
+};
+
+/* Used in e_source_registry_authenticate_sync() */
+struct _AuthContext {
+	ESourceAuthenticator *auth;
+	EDBusAuthenticator *dbus_auth;
+	GCancellable *cancellable;
+	GMainLoop *main_loop;
+	ESourceAuthenticationResult auth_result;
+	GcrSecretExchange *secret_exchange;
+	gboolean authenticating;
+	gboolean success;
+	GError **error;
+};
+
+struct _CreateContext {
+	ESourceRegistry *registry;
+	GMainContext *main_context;
+	GMainLoop *main_loop;
+	gchar *object_path;
+	ESource *source;
+};
+
+struct _SourceClosure {
+	ESourceRegistry *registry;
+	ESource *source;
+};
+
+struct _ThreadClosure {
+	ESourceRegistry *registry;
+	GMainContext *main_context;
+	GMainLoop *main_loop;
+	GCond *main_loop_cond;
+	GMutex *main_loop_mutex;
+};
+
+enum {
+	PROP_0,
+	PROP_DEFAULT_ADDRESS_BOOK,
+	PROP_DEFAULT_CALENDAR,
+	PROP_DEFAULT_MAIL_ACCOUNT,
+	PROP_DEFAULT_MAIL_IDENTITY,
+	PROP_DEFAULT_MEMO_LIST,
+	PROP_DEFAULT_TASK_LIST
+};
+
+enum {
+	SOURCE_ADDED,
+	SOURCE_CHANGED,
+	SOURCE_REMOVED,
+	SOURCE_ENABLED,
+	SOURCE_DISABLED,
+	LAST_SIGNAL
+};
+
+/* Forward Declarations */
+static void	source_registry_add_source	(ESourceRegistry *registry,
+						 ESource *source);
+static void	e_source_registry_initable_init	(GInitableIface *interface);
+
+static guint signals[LAST_SIGNAL];
+
+/* By default, the GAsyncInitable interface calls GInitable.init()
+ * from a separate thread, so we only have to override GInitable. */
+G_DEFINE_TYPE_WITH_CODE (
+	ESourceRegistry,
+	e_source_registry,
+	G_TYPE_OBJECT,
+	G_IMPLEMENT_INTERFACE (
+		G_TYPE_INITABLE, e_source_registry_initable_init)
+	G_IMPLEMENT_INTERFACE (
+		G_TYPE_ASYNC_INITABLE, NULL))
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+	if (async_context->input_source != NULL)
+		g_object_unref (async_context->input_source);
+
+	if (async_context->output_source != NULL)
+		g_object_unref (async_context->output_source);
+
+	if (async_context->auth != NULL)
+		g_object_unref (async_context->auth);
+
+	g_slice_free (AsyncContext, async_context);
+}
+
+static void
+auth_context_free (AuthContext *auth_context)
+{
+	if (auth_context->auth != NULL)
+		g_object_unref (auth_context->auth);
+
+	if (auth_context->dbus_auth != NULL)
+		g_object_unref (auth_context->dbus_auth);
+
+	if (auth_context->cancellable != NULL)
+		g_object_unref (auth_context->cancellable);
+
+	if (auth_context->main_loop != NULL)
+		g_main_loop_unref (auth_context->main_loop);
+
+	if (auth_context->secret_exchange != NULL)
+		g_object_unref (auth_context->secret_exchange);
+
+	g_slice_free (AuthContext, auth_context);
+}
+
+static CreateContext *
+create_context_new (ESourceRegistry *registry)
+{
+	CreateContext *create_context;
+
+	create_context = g_slice_new0 (CreateContext);
+	create_context->registry = g_object_ref (registry);
+	create_context->main_context = g_main_context_new ();
+	create_context->main_loop = g_main_loop_new (
+		create_context->main_context, FALSE);
+
+	g_main_context_push_thread_default (create_context->main_context);
+
+	return create_context;
+}
+
+static void
+create_context_free (CreateContext *create_context)
+{
+	g_main_context_pop_thread_default (create_context->main_context);
+
+	g_object_unref (create_context->registry);
+	g_main_context_unref (create_context->main_context);
+	g_main_loop_unref (create_context->main_loop);
+
+	if (create_context->source != NULL)
+		g_object_unref (create_context->source);
+
+	g_free (create_context->object_path);
+
+	g_slice_free (CreateContext, create_context);
+}
+
+static void
+source_closure_free (SourceClosure *closure)
+{
+	g_object_unref (closure->registry);
+	g_object_unref (closure->source);
+
+	g_slice_free (SourceClosure, closure);
+}
+
+static void
+thread_closure_free (ThreadClosure *closure)
+{
+	/* The registry member is not referenced. */
+
+	g_main_context_unref (closure->main_context);
+	g_main_loop_unref (closure->main_loop);
+	g_cond_free (closure->main_loop_cond);
+	g_mutex_free (closure->main_loop_mutex);
+
+	g_slice_free (ThreadClosure, closure);
+}
+
+static void
+source_registry_object_path_table_insert (ESourceRegistry *registry,
+                                          const gchar *object_path,
+                                          ESource *source)
+{
+	g_return_if_fail (object_path != NULL);
+	g_return_if_fail (E_IS_SOURCE (source));
+
+	g_mutex_lock (registry->priv->object_path_table_lock);
+
+	g_hash_table_insert (
+		registry->priv->object_path_table,
+		g_strdup (object_path),
+		g_object_ref (source));
+
+	g_mutex_unlock (registry->priv->object_path_table_lock);
+}
+
+static ESource *
+source_registry_object_path_table_lookup (ESourceRegistry *registry,
+                                          const gchar *object_path)
+{
+	ESource *source;
+
+	g_return_val_if_fail (object_path != NULL, NULL);
+
+	g_mutex_lock (registry->priv->object_path_table_lock);
+
+	source = g_hash_table_lookup (
+		registry->priv->object_path_table, object_path);
+	if (source != NULL)
+		g_object_ref (source);
+
+	g_mutex_unlock (registry->priv->object_path_table_lock);
+
+	return source;
+}
+
+static gboolean
+source_registry_object_path_table_remove (ESourceRegistry *registry,
+                                          const gchar *object_path)
+{
+	gboolean removed;
+
+	g_return_val_if_fail (object_path != NULL, FALSE);
+
+	g_mutex_lock (registry->priv->object_path_table_lock);
+
+	removed = g_hash_table_remove (
+		registry->priv->object_path_table, object_path);
+
+	g_mutex_unlock (registry->priv->object_path_table_lock);
+
+	return removed;
+}
+
+static void
+source_registry_sources_insert (ESourceRegistry *registry,
+                                ESource *source)
+{
+	const gchar *uid;
+
+	uid = e_source_get_uid (source);
+	g_return_if_fail (uid != NULL);
+
+	g_mutex_lock (registry->priv->sources_lock);
+
+	g_hash_table_insert (
+		registry->priv->sources,
+		g_strdup (uid), g_object_ref (source));
+
+	g_mutex_unlock (registry->priv->sources_lock);
+}
+
+static gboolean
+source_registry_sources_remove (ESourceRegistry *registry,
+                                ESource *source)
+{
+	const gchar *uid;
+	gboolean removed;
+
+	uid = e_source_get_uid (source);
+	g_return_val_if_fail (uid != NULL, FALSE);
+
+	g_mutex_lock (registry->priv->sources_lock);
+
+	removed = g_hash_table_remove (registry->priv->sources, uid);
+
+	g_mutex_unlock (registry->priv->sources_lock);
+
+	return removed;
+}
+
+static ESource *
+source_registry_sources_lookup (ESourceRegistry *registry,
+                                const gchar *uid)
+{
+	ESource *source;
+
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	g_mutex_lock (registry->priv->sources_lock);
+
+	source = g_hash_table_lookup (registry->priv->sources, uid);
+
+	if (source != NULL)
+		g_object_ref (source);
+
+	g_mutex_unlock (registry->priv->sources_lock);
+
+	return source;
+}
+
+static GList *
+source_registry_sources_get_values (ESourceRegistry *registry)
+{
+	GList *values;
+
+	g_mutex_lock (registry->priv->sources_lock);
+
+	values = g_hash_table_get_values (registry->priv->sources);
+
+	g_list_foreach (values, (GFunc) g_object_ref, NULL);
+
+	g_mutex_unlock (registry->priv->sources_lock);
+
+	return values;
+}
+
+static GNode *
+source_registry_sources_build_tree (ESourceRegistry *registry)
+{
+	GNode *root;
+	GHashTable *index;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	g_mutex_lock (registry->priv->sources_lock);
+
+	root = g_node_new (NULL);
+	index = g_hash_table_new (g_str_hash, g_str_equal);
+
+	/* Add a GNode for each ESource to the index. */
+	g_hash_table_iter_init (&iter, registry->priv->sources);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		ESource *source = g_object_ref (value);
+		g_hash_table_insert (index, key, g_node_new (source));
+	}
+
+	/* Traverse the index and link the nodes together. */
+	g_hash_table_iter_init (&iter, index);
+	while (g_hash_table_iter_next (&iter, NULL, &value)) {
+		ESource *source;
+		GNode *source_node;
+		GNode *parent_node;
+		const gchar *parent_uid;
+
+		source_node = (GNode *) value;
+		source = E_SOURCE (source_node->data);
+		parent_uid = e_source_get_parent (source);
+
+		if (parent_uid == NULL || *parent_uid == '\0') {
+			parent_node = root;
+		} else {
+			parent_node = g_hash_table_lookup (index, parent_uid);
+			g_warn_if_fail (parent_node != NULL);
+		}
+
+		/* Should never be NULL, but just to be safe. */
+		if (parent_node != NULL)
+			g_node_append (parent_node, source_node);
+	}
+
+	g_hash_table_destroy (index);
+
+	g_mutex_unlock (registry->priv->sources_lock);
+
+	return root;
+}
+
+static void
+source_registry_settings_changed_cb (GSettings *settings,
+                                     const gchar *key,
+                                     ESourceRegistry *registry)
+{
+	/* We define a property name that matches every key in
+	 * the "org.gnome.Evolution.DefaultSources" schema. */
+	g_object_notify (G_OBJECT (registry), key);
+}
+
+static gboolean
+source_registry_source_changed_idle_cb (gpointer user_data)
+{
+	SourceClosure *closure = user_data;
+
+	g_signal_emit (
+		closure->registry,
+		signals[SOURCE_CHANGED], 0,
+		closure->source);
+
+	return FALSE;
+}
+
+static gboolean
+source_registry_source_notify_enabled_idle_cb (gpointer user_data)
+{
+	SourceClosure *closure = user_data;
+
+	if (e_source_get_enabled (closure->source))
+		g_signal_emit (
+			closure->registry,
+			signals[SOURCE_ENABLED], 0,
+			closure->source);
+	else
+		g_signal_emit (
+			closure->registry,
+			signals[SOURCE_DISABLED], 0,
+			closure->source);
+
+	return FALSE;
+}
+
+static void
+source_registry_source_changed_cb (ESource *source,
+                                   ESourceRegistry *registry)
+{
+	GSource *idle_source;
+	SourceClosure *closure;
+
+	closure = g_slice_new0 (SourceClosure);
+	closure->registry = g_object_ref (registry);
+	closure->source = g_object_ref (source);
+
+	idle_source = g_idle_source_new ();
+	g_source_set_callback (
+		idle_source,
+		source_registry_source_changed_idle_cb,
+		closure, (GDestroyNotify) source_closure_free);
+	g_source_attach (idle_source, registry->priv->main_context);
+	g_source_unref (idle_source);
+}
+
+static void
+source_registry_source_notify_enabled_cb (ESource *source,
+                                          GParamSpec *pspec,
+                                          ESourceRegistry *registry)
+{
+	GSource *idle_source;
+	SourceClosure *closure;
+
+	closure = g_slice_new0 (SourceClosure);
+	closure->registry = g_object_ref (registry);
+	closure->source = g_object_ref (source);
+
+	idle_source = g_idle_source_new ();
+	g_source_set_callback (
+		idle_source,
+		source_registry_source_notify_enabled_idle_cb,
+		closure, (GDestroyNotify) source_closure_free);
+	g_source_attach (idle_source, registry->priv->main_context);
+	g_source_unref (idle_source);
+}
+
+static void
+source_registry_unref_source (ESource *source)
+{
+	g_signal_handlers_disconnect_matched (
+		source, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+		source_registry_source_changed_cb, NULL);
+
+	g_signal_handlers_disconnect_matched (
+		source, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+		source_registry_source_notify_enabled_cb, NULL);
+
+	g_object_unref (source);
+}
+
+static void
+source_registry_add_source (ESourceRegistry *registry,
+                            ESource *source)
+{
+	const gchar *uid;
+
+	uid = e_source_get_uid (source);
+	g_return_if_fail (uid != NULL);
+
+	g_mutex_lock (registry->priv->sources_lock);
+
+	/* Check if we already have this source in the registry. */
+	if (g_hash_table_lookup (registry->priv->sources, uid) != NULL) {
+		g_mutex_unlock (registry->priv->sources_lock);
+		return;
+	}
+
+	g_signal_connect (
+		source, "changed",
+		G_CALLBACK (source_registry_source_changed_cb),
+		registry);
+
+	g_signal_connect (
+		source, "notify::enabled",
+		G_CALLBACK (source_registry_source_notify_enabled_cb),
+		registry);
+
+	g_mutex_unlock (registry->priv->sources_lock);
+
+	source_registry_sources_insert (registry, source);
+
+	g_signal_emit (registry, signals[SOURCE_ADDED], 0, source);
+}
+
+static void
+source_registry_remove_source (ESourceRegistry *registry,
+                               ESource *source)
+{
+	g_object_ref (source);
+
+	if (source_registry_sources_remove (registry, source))
+		g_signal_emit (registry, signals[SOURCE_REMOVED], 0, source);
+
+	g_object_unref (source);
+}
+
+static gboolean
+source_registry_object_added_idle_cb (gpointer user_data)
+{
+	SourceClosure *closure = user_data;
+
+	source_registry_add_source (closure->registry, closure->source);
+
+	return FALSE;
+}
+
+static void
+source_registry_object_added_cb (GDBusObjectManager *object_manager,
+                                 GDBusObject *dbus_object,
+                                 ESourceRegistry *registry)
+{
+	SourceClosure *closure;
+	GMainContext *main_context;
+	GSource *idle_source;
+	ESource *source;
+	const gchar *object_path;
+	GError *error = NULL;
+
+	g_return_if_fail (E_DBUS_IS_OBJECT (dbus_object));
+
+	/* We don't want the ESource emitting "changed" signals from
+	 * the manager thread, so we pass it the same main context the
+	 * registry uses for scheduling signal emissions. */
+	main_context = registry->priv->main_context;
+	source = e_source_new (dbus_object, main_context, &error);
+	object_path = g_dbus_object_get_object_path (dbus_object);
+
+	/* The likelihood of an error here is slim, so it's
+	 * sufficient to just print a warning if one occurs. */
+	if (error != NULL) {
+		g_warn_if_fail (source == NULL);
+		g_critical (
+			"ESourceRegistry: Failed to create a "
+			"data source object for path '%s': %s",
+			object_path, error->message);
+		g_error_free (error);
+		return;
+	}
+
+	g_return_if_fail (E_IS_SOURCE (source));
+
+	/* Add the ESource to the object path table immediately. */
+	source_registry_object_path_table_insert (
+		registry, object_path, source);
+
+	/* Schedule a callback on the ESourceRegistry's GMainContext. */
+
+	closure = g_slice_new0 (SourceClosure);
+	closure->registry = g_object_ref (registry);
+	closure->source = g_object_ref (source);
+
+	idle_source = g_idle_source_new ();
+	g_source_set_callback (
+		idle_source,
+		source_registry_object_added_idle_cb,
+		closure, (GDestroyNotify) source_closure_free);
+	g_source_attach (idle_source, registry->priv->main_context);
+	g_source_unref (idle_source);
+
+	g_object_unref (source);
+}
+
+static gboolean
+source_registry_object_removed_idle_cb (gpointer user_data)
+{
+	SourceClosure *closure = user_data;
+
+	source_registry_remove_source (closure->registry, closure->source);
+
+	return FALSE;
+}
+
+static void
+source_registry_object_removed_cb (GDBusObjectManager *manager,
+                                   GDBusObject *dbus_object,
+                                   ESourceRegistry *registry)
+{
+	SourceClosure *closure;
+	GSource *idle_source;
+	ESource *source;
+	const gchar *object_path;
+
+	/* Find the corresponding ESource in the object path table.
+	 * Note that the lookup returns a new ESource reference. */
+	object_path = g_dbus_object_get_object_path (dbus_object);
+	source = source_registry_object_path_table_lookup (
+		registry, object_path);
+	g_return_if_fail (E_IS_SOURCE (source));
+
+	/* Remove the ESource from the object path table immediately. */
+	source_registry_object_path_table_remove (registry, object_path);
+
+	/* Schedule a callback on the ESourceRegistry's GMainContext. */
+
+	closure = g_slice_new0 (SourceClosure);
+	closure->registry = g_object_ref (registry);
+	closure->source = g_object_ref (source);
+
+	idle_source = g_idle_source_new ();
+	g_source_set_callback (
+		idle_source,
+		source_registry_object_removed_idle_cb,
+		closure, (GDestroyNotify) source_closure_free);
+	g_source_attach (idle_source, registry->priv->main_context);
+	g_source_unref (idle_source);
+
+	g_object_unref (source);
+}
+
+static gboolean
+source_registry_object_manager_running (gpointer data)
+{
+	ThreadClosure *closure = data;
+
+	g_mutex_lock (closure->main_loop_mutex);
+	g_cond_broadcast (closure->main_loop_cond);
+	g_mutex_unlock (closure->main_loop_mutex);
+
+	return FALSE;
+}
+
+static gpointer
+source_registry_object_manager_thread (gpointer data)
+{
+	GDBusObjectManager *object_manager;
+	ThreadClosure *closure = data;
+	GSource *idle_source;
+	GList *list, *link;
+	gulong object_added_id;
+	gulong object_removed_id;
+	GError *error = NULL;
+
+	/* GDBusObjectManagerClient grabs the thread-default GMainContext
+	 * at creation time and only emits signals from that GMainContext.
+	 * Since we need to synchronize with its "object-added" signal as
+	 * part of our create_source() operation, we give the client its
+	 * own dedicated thread to ensure its signal emissions can never
+	 * be inhibited by someone overriding the thread-default context. */
+
+	/* This becomes the GMainContext that GDBusObjectManagerClient
+	 * will emit signals from.  Make it the thread-default context
+	 * for this thread before creating the client. */
+	g_main_context_push_thread_default (closure->main_context);
+
+	object_manager = e_dbus_object_manager_client_new_for_bus_sync (
+		G_BUS_TYPE_SESSION,
+		G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+		SOURCES_DBUS_SERVICE_NAME,
+		DBUS_OBJECT_PATH,
+		NULL, &error);
+
+	/* If this fails there's really no point in continuing
+	 * since we rely on the object manager to populate the
+	 * registry.  Abort the process with a fatal error. */
+	if (error != NULL) {
+		g_error ("%s", error->message);
+		g_assert_not_reached ();
+	}
+
+	/* Give the registry a handle to the object manager. */
+	closure->registry->priv->dbus_object_manager =
+		g_object_ref (object_manager);
+
+	/* Now populate the registry with an initial set of ESources. */
+
+	list = g_dbus_object_manager_get_objects (object_manager);
+
+	for (link = list; link != NULL; link = g_list_next (link))
+		source_registry_object_added_cb (
+			object_manager,
+			G_DBUS_OBJECT (link->data),
+			closure->registry);
+
+	g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+	/* Schedule a one-time idle callback to broadcast through a
+	 * condition variable that our main loop is up and running. */
+
+	idle_source = g_idle_source_new ();
+	g_source_set_callback (
+		idle_source,
+		source_registry_object_manager_running,
+		closure, (GDestroyNotify) NULL);
+	g_source_attach (idle_source, closure->main_context);
+	g_source_unref (idle_source);
+
+	/* Listen for D-Bus object additions and removals. */
+
+	object_added_id = g_signal_connect (
+		object_manager, "object-added",
+		G_CALLBACK (source_registry_object_added_cb),
+		closure->registry);
+
+	object_removed_id = g_signal_connect (
+		object_manager, "object-removed",
+		G_CALLBACK (source_registry_object_removed_cb),
+		closure->registry);
+
+	/* Now we mostly idle here for the rest of the session. */
+
+	g_main_loop_run (closure->main_loop);
+
+	/* Clean up and exit. */
+
+	g_signal_handler_disconnect (object_manager, object_added_id);
+	g_signal_handler_disconnect (object_manager, object_removed_id);
+
+	g_object_unref (object_manager);
+
+	g_main_context_pop_thread_default (closure->main_context);
+
+	return NULL;
+}
+
+static void
+source_registry_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_DEFAULT_ADDRESS_BOOK:
+			e_source_registry_set_default_address_book (
+				E_SOURCE_REGISTRY (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_DEFAULT_CALENDAR:
+			e_source_registry_set_default_calendar (
+				E_SOURCE_REGISTRY (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_DEFAULT_MAIL_ACCOUNT:
+			e_source_registry_set_default_mail_account (
+				E_SOURCE_REGISTRY (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_DEFAULT_MAIL_IDENTITY:
+			e_source_registry_set_default_mail_identity (
+				E_SOURCE_REGISTRY (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_DEFAULT_MEMO_LIST:
+			e_source_registry_set_default_memo_list (
+				E_SOURCE_REGISTRY (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_DEFAULT_TASK_LIST:
+			e_source_registry_set_default_task_list (
+				E_SOURCE_REGISTRY (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_registry_get_property (GObject *object,
+                              guint property_id,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_DEFAULT_ADDRESS_BOOK:
+			g_value_take_object (
+				value,
+				e_source_registry_ref_default_address_book (
+				E_SOURCE_REGISTRY (object)));
+			return;
+
+		case PROP_DEFAULT_CALENDAR:
+			g_value_take_object (
+				value,
+				e_source_registry_ref_default_calendar (
+				E_SOURCE_REGISTRY (object)));
+			return;
+
+		case PROP_DEFAULT_MAIL_ACCOUNT:
+			g_value_take_object (
+				value,
+				e_source_registry_ref_default_mail_account (
+				E_SOURCE_REGISTRY (object)));
+			return;
+
+		case PROP_DEFAULT_MAIL_IDENTITY:
+			g_value_take_object (
+				value,
+				e_source_registry_ref_default_mail_identity (
+				E_SOURCE_REGISTRY (object)));
+			return;
+
+		case PROP_DEFAULT_MEMO_LIST:
+			g_value_take_object (
+				value,
+				e_source_registry_ref_default_memo_list (
+				E_SOURCE_REGISTRY (object)));
+			return;
+
+		case PROP_DEFAULT_TASK_LIST:
+			g_value_take_object (
+				value,
+				e_source_registry_ref_default_task_list (
+				E_SOURCE_REGISTRY (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_registry_dispose (GObject *object)
+{
+	ESourceRegistryPrivate *priv;
+
+	priv = E_SOURCE_REGISTRY_GET_PRIVATE (object);
+
+	/* Terminate the manager thread first. */
+	if (priv->manager_thread != NULL) {
+		g_main_loop_quit (priv->thread_closure->main_loop);
+		g_thread_join (priv->manager_thread);
+		thread_closure_free (priv->thread_closure);
+		priv->manager_thread = NULL;
+		priv->thread_closure = NULL;
+	}
+
+	if (priv->main_context != NULL) {
+		g_main_context_unref (priv->main_context);
+		priv->main_context = NULL;
+	}
+
+	if (priv->dbus_object_manager != NULL) {
+		g_object_unref (priv->dbus_object_manager);
+		priv->dbus_object_manager = NULL;
+	}
+
+	if (priv->dbus_source_manager != NULL) {
+		g_object_unref (priv->dbus_source_manager);
+		priv->dbus_source_manager = NULL;
+	}
+
+	g_hash_table_remove_all (priv->object_path_table);
+
+	g_hash_table_remove_all (priv->sources);
+
+	if (priv->settings != NULL) {
+		g_object_unref (priv->settings);
+		priv->settings = NULL;
+	}
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_registry_parent_class)->dispose (object);
+}
+
+static void
+source_registry_finalize (GObject *object)
+{
+	ESourceRegistryPrivate *priv;
+
+	priv = E_SOURCE_REGISTRY_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->object_path_table);
+	g_mutex_free (priv->object_path_table_lock);
+
+	g_hash_table_destroy (priv->sources);
+	g_mutex_free (priv->sources_lock);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_registry_parent_class)->finalize (object);
+}
+
+static gboolean
+source_registry_initable_init (GInitable *initable,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	ESourceRegistry *registry;
+	ThreadClosure *closure;
+
+	registry = E_SOURCE_REGISTRY (initable);
+
+	closure = g_slice_new0 (ThreadClosure);
+	closure->registry = registry;  /* do not reference */
+	closure->main_context = g_main_context_new ();
+	/* It's important to pass 'is_running=FALSE' here because
+	 * we wait for the main loop to start running as a way of
+	 * synchronizing with the manager thread. */
+	closure->main_loop = g_main_loop_new (closure->main_context, FALSE);
+	closure->main_loop_cond = g_cond_new ();
+	closure->main_loop_mutex = g_mutex_new ();
+
+	registry->priv->thread_closure = closure;
+
+	registry->priv->manager_thread = g_thread_create (
+		source_registry_object_manager_thread,
+		closure, TRUE /* joinable */, error);
+
+	if (registry->priv->manager_thread == NULL)
+		return FALSE;
+
+	/* Wait for notification that the manager
+	 * thread's main loop has been started. */
+	g_mutex_lock (closure->main_loop_mutex);
+	while (!g_main_loop_is_running (closure->main_loop))
+		g_cond_wait (
+			closure->main_loop_cond,
+			closure->main_loop_mutex);
+	g_mutex_unlock (closure->main_loop_mutex);
+
+	/* We should now have a GDBusObjectManagerClient available. */
+	g_return_val_if_fail (
+		G_IS_DBUS_OBJECT_MANAGER_CLIENT (
+		registry->priv->dbus_object_manager), FALSE);
+
+	/* The manager thread will have queued up a bunch of idle
+	 * sources on our GMainContext to populate the registry.
+	 * Iterate our GMainContext until they get dispatched. */
+	while (g_hash_table_size (registry->priv->sources) == 0)
+		g_main_context_iteration (registry->priv->main_context, TRUE);
+
+	/* The EDBusSourceManagerProxy is just another D-Bus interface
+	 * that resides at the same object path.  It's unrelated to the
+	 * GDBusObjectManagerClient and doesn't need its own thread. */
+	registry->priv->dbus_source_manager =
+		e_dbus_source_manager_proxy_new_for_bus_sync (
+			G_BUS_TYPE_SESSION,
+			G_DBUS_PROXY_FLAGS_NONE,
+			SOURCES_DBUS_SERVICE_NAME,
+			DBUS_OBJECT_PATH,
+			cancellable, error);
+
+	if (registry->priv->dbus_source_manager == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+e_source_registry_class_init (ESourceRegistryClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESourceRegistryPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_registry_set_property;
+	object_class->get_property = source_registry_get_property;
+	object_class->dispose = source_registry_dispose;
+	object_class->finalize = source_registry_finalize;
+
+	/* The property names correspond to the key names in the
+	 * "org.gnome.Evolution.DefaultSources" GSettings schema. */
+
+	/**
+	 * ESourceRegistry:default-address-book
+	 *
+	 * The default address book #ESource.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_DEFAULT_ADDRESS_BOOK,
+		g_param_spec_object (
+			"default-address-book",
+			"Default Address Book",
+			"The default address book ESource",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * ESourceRegistry:default-calendar
+	 *
+	 * The default calendar #ESource.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_DEFAULT_CALENDAR,
+		g_param_spec_object (
+			"default-calendar",
+			"Default Calendar",
+			"The default calendar ESource",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * ESourceRegistry:default-mail-account
+	 *
+	 * The default mail account #ESource.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_DEFAULT_MAIL_ACCOUNT,
+		g_param_spec_object (
+			"default-mail-account",
+			"Default Mail Account",
+			"The default mail account ESource",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * ESourceRegistry:default-mail-identity
+	 *
+	 * The default mail identity #ESource.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_DEFAULT_MAIL_IDENTITY,
+		g_param_spec_object (
+			"default-mail-identity",
+			"Default Mail Identity",
+			"The default mail identity ESource",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * ESourceRegistry:default-memo-list
+	 *
+	 * The default memo list #ESource.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_DEFAULT_MEMO_LIST,
+		g_param_spec_object (
+			"default-memo-list",
+			"Default Memo List",
+			"The default memo list ESource",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * ESourceRegistry:default-task-list
+	 *
+	 * The default task list #ESource.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_DEFAULT_TASK_LIST,
+		g_param_spec_object (
+			"default-task-list",
+			"Default Task List",
+			"The default task list ESource",
+			E_TYPE_SOURCE,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * ESourceRegistry::source-added:
+	 * @registry: the #ESourceRegistry which emitted the signal
+	 * @source: the newly-added #ESource
+	 *
+	 * Emitted when an #ESource is added to @registry.
+	 **/
+	signals[SOURCE_ADDED] = g_signal_new (
+		"source-added",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESourceRegistryClass, source_added),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SOURCE);
+
+	/**
+	 * ESourceRegistry::source-changed
+	 * @registry: the #ESourceRegistry which emitted the signal
+	 * @source: the #ESource that changed
+	 *
+	 * Emitted when an #ESource registered with @registry emits
+	 * its #ESource::changed signal.
+	 **/
+	signals[SOURCE_CHANGED] = g_signal_new (
+		"source-changed",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESourceRegistryClass, source_changed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SOURCE);
+
+	/**
+	 * ESourceRegistry::source-removed:
+	 * @registry: the #ESourceRegistry which emitted the signal
+	 * @source: the #ESource that got removed
+	 *
+	 * Emitted when an #ESource is removed from @registry.
+	 **/
+	signals[SOURCE_REMOVED] = g_signal_new (
+		"source-removed",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESourceRegistryClass, source_removed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SOURCE);
+
+	/**
+	 * ESourceRegistry::source-enabled:
+	 * @registry: the #ESourceRegistry which emitted the signal
+	 * @source: the #ESource that got enabled
+	 *
+	 * Emitted when an #ESource #ESource:enabled property becomes %TRUE.
+	 **/
+	signals[SOURCE_ENABLED] = g_signal_new (
+		"source-enabled",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESourceRegistryClass, source_enabled),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SOURCE);
+
+	/**
+	 * ESourceRegistry::source-disabled:
+	 * @registry: the #ESourceRegistry which emitted the signal
+	 * @source: the #ESource that got disabled
+	 *
+	 * Emitted when an #ESource #ESource:enabled property becomes %FALSE.
+	 **/
+	signals[SOURCE_DISABLED] = g_signal_new (
+		"source-disabled",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESourceRegistryClass, source_disabled),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SOURCE);
+}
+
+static void
+e_source_registry_initable_init (GInitableIface *interface)
+{
+	interface->init = source_registry_initable_init;
+}
+
+static void
+e_source_registry_init (ESourceRegistry *registry)
+{
+	registry->priv = E_SOURCE_REGISTRY_GET_PRIVATE (registry);
+
+	/* This is so the object manager thread can schedule signal
+	 * emissions on the thread-default context for this thread. */
+	registry->priv->main_context = g_main_context_get_thread_default ();
+	if (registry->priv->main_context != NULL)
+		g_main_context_ref (registry->priv->main_context);
+
+	/* D-Bus object path -> ESource */
+	registry->priv->object_path_table =
+		g_hash_table_new_full (
+			(GHashFunc) g_str_hash,
+			(GEqualFunc) g_str_equal,
+			(GDestroyNotify) g_free,
+			(GDestroyNotify) g_object_unref);
+
+	registry->priv->object_path_table_lock = g_mutex_new ();
+
+	/* UID string -> ESource */
+	registry->priv->sources = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) source_registry_unref_source);
+
+	registry->priv->sources_lock = g_mutex_new ();
+
+	registry->priv->settings = g_settings_new (GSETTINGS_SCHEMA);
+
+	g_signal_connect (
+		registry->priv->settings, "changed",
+		G_CALLBACK (source_registry_settings_changed_cb), registry);
+}
+
+/**
+ * e_source_registry_new_sync:
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a new #ESourceRegistry front-end for the registry D-Bus service.
+ * If an error occurs in connecting to the D-Bus service, the function sets
+ * @error and returns %NULL.
+ *
+ * Returns: a new #ESourceRegistry, or %NULL
+ *
+ * Since: 3.6
+ **/
+ESourceRegistry *
+e_source_registry_new_sync (GCancellable *cancellable,
+                            GError **error)
+{
+	return g_initable_new (
+		E_TYPE_SOURCE_REGISTRY,
+		cancellable, error, NULL);
+}
+
+/**
+ * e_source_registry_new:
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously creates a new #ESourceRegistry front-end for the registry
+ * D-Bus service.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_registry_new_finish() to get the result of the operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_new (GCancellable *cancellable,
+                       GAsyncReadyCallback callback,
+                       gpointer user_data)
+{
+	g_async_initable_new_async (
+		E_TYPE_SOURCE_REGISTRY,
+		G_PRIORITY_DEFAULT, cancellable,
+		callback, user_data, NULL);
+}
+
+/**
+ * e_source_registry_new_finish:
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_registry_new_finish().
+ * If an error occurs in connecting to the D-Bus service, the function
+ * sets @error and returns %NULL.
+ *
+ * Returns: a new #ESourceRegistry, or %NULL
+ *
+ * Since: 3.6
+ **/
+ESourceRegistry *
+e_source_registry_new_finish (GAsyncResult *result,
+                              GError **error)
+{
+	GObject *source_object;
+	GObject *object;
+
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+	source_object = g_async_result_get_source_object (result);
+	g_return_val_if_fail (source_object != NULL, NULL);
+
+	object = g_async_initable_new_finish (
+		G_ASYNC_INITABLE (source_object), result, error);
+
+	g_object_unref (source_object);
+
+	return (object != NULL) ? E_SOURCE_REGISTRY (object) : NULL;
+}
+
+/* Helper for e_source_registry_authenticate() */
+static void
+source_registry_authenticate_thread (GSimpleAsyncResult *simple,
+                                     GObject *object,
+                                     GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_registry_authenticate_sync (
+		E_SOURCE_REGISTRY (object),
+		async_context->input_source,
+		async_context->auth,
+		cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/* Helper for e_source_registry_authenticate_sync() */
+static gboolean
+source_registry_authenticate_respond_cb (AuthContext *auth_context)
+{
+	ESourceAuthenticationResult auth_result;
+	GError *non_fatal_error = NULL;
+
+	g_return_val_if_fail (auth_context->authenticating, FALSE);
+
+	auth_result = auth_context->auth_result;
+
+	/* Allow the next authentication attempt to proceed. */
+	auth_context->authenticating = FALSE;
+
+	/* Send the server a status update based on the authentication
+	 * result.  Note, we don't really care if the D-Bus message gets
+	 * through to the server at this point.  If it doesn't, the auth
+	 * session will either time out on its own or the authentication
+	 * dialog will eventually be dismissed by the user. */
+
+	/* If an error occurred while attempting to authenticate,
+	 * tell the server to cancel the authentication session. */
+	if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) {
+		e_dbus_authenticator_call_cancel_sync (
+			auth_context->dbus_auth,
+			auth_context->cancellable,
+			&non_fatal_error);
+		g_main_loop_quit (auth_context->main_loop);
+		auth_context->success = FALSE;
+
+	/* If the password was accepted, let the server know so it
+	 * can close any authentication dialogs and save the user
+	 * provided password to the keyring. */
+	} else if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
+		e_dbus_authenticator_call_accepted_sync (
+			auth_context->dbus_auth,
+			auth_context->cancellable,
+			&non_fatal_error);
+		g_main_loop_quit (auth_context->main_loop);
+		auth_context->success = TRUE;
+
+	/* If the password was rejected, let the server know so it can
+	 * indicate failure and request a different password, and then
+	 * wait for the next "response" signal. */
+	} else {
+		e_dbus_authenticator_call_rejected_sync (
+			auth_context->dbus_auth,
+			auth_context->cancellable,
+			&non_fatal_error);
+	}
+
+	/* Leave breadcrumbs if something went wrong,
+	 * but don't fail the whole operation over it. */
+	if (non_fatal_error != NULL) {
+		g_warning ("%s: %s", G_STRFUNC, non_fatal_error->message);
+		g_error_free (non_fatal_error);
+	}
+
+	return FALSE;
+}
+
+/* Helper for e_source_registry_authenticate_sync() */
+static void
+source_registry_authenticate_authenticate_cb (EDBusAuthenticator *dbus_auth,
+                                              const gchar *encrypted_secret,
+                                              AuthContext *auth_context)
+{
+	GSource *idle_source;
+	GMainContext *main_context;
+	GString *password;
+	gboolean valid_secret;
+
+	/* We should only get one secret at a time. */
+	g_return_if_fail (!auth_context->authenticating);
+
+	valid_secret = gcr_secret_exchange_receive (
+		auth_context->secret_exchange, encrypted_secret);
+	g_return_if_fail (valid_secret);
+
+	auth_context->authenticating = TRUE;
+
+	/* This avoids revealing the password in a stack trace. */
+	password = g_string_new (
+		gcr_secret_exchange_get_secret (
+		auth_context->secret_exchange, NULL));
+
+	/* Try authenticating with the given password.  We have to
+	 * call this synchronously because some authenticators use
+	 * mutexes to serialize I/O operations and are not prepared
+	 * to make authentication attempts from a different thread.
+	 *
+	 * Unfortunately this means we won't notice server-side
+	 * dismissals while the main loop is blocked.  We respond
+	 * to the server from a low-priority idle callback so that
+	 * any pending "dismissed" signals get handled first. */
+
+	auth_context->auth_result =
+		e_source_authenticator_try_password_sync (
+			auth_context->auth, password,
+			auth_context->cancellable,
+			auth_context->error);
+
+	idle_source = g_idle_source_new ();
+	main_context = g_main_context_get_thread_default ();
+	g_source_set_callback (
+		idle_source, (GSourceFunc)
+		source_registry_authenticate_respond_cb,
+		auth_context, NULL);
+	g_source_attach (idle_source, main_context);
+	g_source_unref (idle_source);
+
+	g_string_free (password, TRUE);
+}
+
+/* Helper for e_source_registry_authenticate_sync() */
+static void
+source_registry_authenticate_dismissed_cb (EDBusAuthenticator *dbus_auth,
+                                           AuthContext *auth_context)
+{
+	/* Be careful not to overwrite an existing error in case this
+	 * is called after e_source_authenticator_try_password_sync()
+	 * but prior to the idle callback. */
+	if (auth_context->auth_result != E_SOURCE_AUTHENTICATION_ERROR) {
+		/* XXX Use a separate error code for dismissals? */
+		g_set_error_literal (
+			auth_context->error,
+			G_IO_ERROR, G_IO_ERROR_CANCELLED,
+			_("The user declined to authenticate"));
+		auth_context->auth_result = E_SOURCE_AUTHENTICATION_ERROR;
+	}
+
+	g_main_loop_quit (auth_context->main_loop);
+	auth_context->success = FALSE;
+}
+
+/* Helper for e_source_registry_authenticate_sync() */
+static gboolean
+source_registry_call_authenticate_for_source (ESourceRegistry *registry,
+                                              ESourceAuthenticator *auth,
+                                              ESource *source,
+                                              gchar **out_object_path,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+	ESource *collection;
+	const gchar *uid;
+	gchar *prompt_title = NULL;
+	gchar *prompt_message = NULL;
+	gchar *prompt_description = NULL;
+	gboolean success;
+
+	/* If the source is a member of a collection, we want to store
+	 * the password under the UID of the "collection" source so it
+	 * will apply to the entire collection.
+	 *
+	 * XXX This assumes all sources in a collection share a single
+	 *     password.  If that turns out not to be true in all cases
+	 *     we could maybe add a "SharedPassword: true/false" key to
+	 *     [Collection] and apply it here.
+	 */
+	collection = e_source_registry_find_extension (
+		registry, source, E_SOURCE_EXTENSION_COLLECTION);
+	if (collection != NULL)
+		source = collection;
+	else
+		g_object_ref (source);
+
+	uid = e_source_get_uid (source);
+
+	e_source_authenticator_get_prompt_strings (
+		auth, source,
+		&prompt_title,
+		&prompt_message,
+		&prompt_description);
+
+	success = e_dbus_source_manager_call_authenticate_sync (
+		registry->priv->dbus_source_manager, uid,
+		prompt_title, prompt_message, prompt_description,
+		out_object_path, cancellable, error);
+
+	g_free (prompt_title);
+	g_free (prompt_message);
+	g_free (prompt_description);
+
+	g_object_unref (source);
+
+	return success;
+}
+
+/**
+ * e_source_registry_authenticate_sync:
+ * @registry: an #ESourceRegistry
+ * @source: an #ESource
+ * @auth: an #ESourceAuthenticator
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Authenticates @source, using @auth to handle the authentication
+ * attempts.  The operation loops until authentication is successful or
+ * the user aborts further authentication attempts.  If an error occurs,
+ * the function will set @error and return %FALSE.
+ *
+ * Note that @source need not have an #GDBusObject, which means this
+ * function can test authentication on a throw-away #ESource.
+ *
+ * Only backend implementations and data source editors should call this
+ * function.  The intent is for basic client applications to not have to
+ * deal with authentication at all.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_authenticate_sync (ESourceRegistry *registry,
+                                     ESource *source,
+                                     ESourceAuthenticator *auth,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+	AuthContext *auth_context;
+	GMainContext *main_context;
+	EDBusAuthenticator *dbus_auth;
+	gchar *encryption_key;
+	gchar *object_path = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth), FALSE);
+
+	/* This extracts authentication prompt details for the ESource
+	 * before initiating an authentication session with the server,
+	 * so split it out of the main algorithm for clarity's sake. */
+	success = source_registry_call_authenticate_for_source (
+		registry, auth, source, &object_path, cancellable, error);
+
+	if (!success) {
+		g_warn_if_fail (object_path == NULL);
+		return FALSE;
+	}
+
+	g_return_val_if_fail (object_path != NULL, FALSE);
+
+	main_context = g_main_context_new ();
+	g_main_context_push_thread_default (main_context);
+
+	dbus_auth = e_dbus_authenticator_proxy_new_for_bus_sync (
+		G_BUS_TYPE_SESSION,
+		G_DBUS_PROXY_FLAGS_NONE,
+		SOURCES_DBUS_SERVICE_NAME,
+		object_path, cancellable, error);
+
+	g_free (object_path);
+
+	if (dbus_auth == NULL) {
+		success = FALSE;
+		goto exit;
+	}
+
+	auth_context = g_slice_new0 (AuthContext);
+	auth_context->auth = g_object_ref (auth);
+	auth_context->dbus_auth = dbus_auth;  /* takes ownership */
+	auth_context->main_loop = g_main_loop_new (main_context, FALSE);
+	auth_context->error = error;
+
+	/* This just needs to be something other than
+	 * E_SOURCE_AUTHENTICATION_ERROR so we don't trip
+	 * up source_registry_authenticate_dismissed_cb(). */
+	auth_context->auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
+
+	if (G_IS_CANCELLABLE (cancellable))
+		auth_context->cancellable = g_object_ref (cancellable);
+
+	auth_context->secret_exchange =
+		gcr_secret_exchange_new (GCR_SECRET_EXCHANGE_PROTOCOL_1);
+
+	g_signal_connect (
+		dbus_auth, "authenticate",
+		G_CALLBACK (source_registry_authenticate_authenticate_cb),
+		auth_context);
+
+	g_signal_connect (
+		dbus_auth, "dismissed",
+		G_CALLBACK (source_registry_authenticate_dismissed_cb),
+		auth_context);
+
+	encryption_key = gcr_secret_exchange_begin (
+		auth_context->secret_exchange);
+
+	/* Signal the D-Bus server that we're ready to begin the
+	 * authentication session.  This must happen AFTER we've
+	 * connected to the response signal since the server may
+	 * already have a response ready and waiting for us. */
+	success = e_dbus_authenticator_call_ready_sync (
+		dbus_auth, encryption_key, cancellable, error);
+
+	g_free (encryption_key);
+
+	if (success) {
+		g_main_loop_run (auth_context->main_loop);
+		success = auth_context->success;
+	}
+
+	auth_context_free (auth_context);
+
+exit:
+	g_main_context_pop_thread_default (main_context);
+	g_main_context_unref (main_context);
+
+	return success;
+}
+
+/**
+ * e_source_registry_authenticate:
+ * @registry: an #ESourceRegistry
+ * @source: an #ESource
+ * @auth: an #ESourceAuthenticator
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously authenticates @source, using @auth to handle the
+ * authentication attempts.  The operation loops until authentication
+ * is successful or the user aborts further authentication attempts.
+ *
+ * Note that @source need not have an #GDBusObject, which means this
+ * function can test authentication on a throw-away #ESource.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_registry_authenticate_finish() to get the result of the
+ * operation.
+ *
+ * Only backend implementations and data source editors should call this
+ * function.  The intent is for basic client applications to not have to
+ * deal with authentication at all.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_authenticate (ESourceRegistry *registry,
+                                ESource *source,
+                                ESourceAuthenticator *auth,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->input_source = g_object_ref (source);
+	async_context->auth = g_object_ref (auth);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (registry), callback, user_data,
+		e_source_registry_authenticate);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_registry_authenticate_thread,
+		G_PRIORITY_DEFAULT, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_registry_authenticate_finish:
+ * @registry: an #ESourceRegistry
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_registry_authenticate().
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_authenticate_finish (ESourceRegistry *registry,
+                                       GAsyncResult *result,
+                                       GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (registry),
+		e_source_registry_authenticate), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/* Helper for e_source_registry_commit_source() */
+static void
+source_registry_commit_source_thread (GSimpleAsyncResult *simple,
+                                      GObject *object,
+                                      GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_registry_commit_source_sync (
+		E_SOURCE_REGISTRY (object),
+		async_context->input_source,
+		&async_context->output_source,
+		cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/**
+ * e_source_registry_commit_source_sync:
+ * @registry: an #ESourceRegistry
+ * @input_source: an #ESource with changes to commit
+ * @output_source: return location for the committed #ESource, or %NULL
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for #GError, or %NULL
+ *
+ * This is a convenience function intended for use with graphical
+ * #ESource editors.  Call this function when the user is finished
+ * making changes to @input_source.
+ *
+ * If @input_source has an #GDBusObject, its contents are submitted to
+ * the D-Bus service through e_source_write_sync().  If the operation is
+ * successful and @output_source is non-%NULL, @output_source will be set
+ * to @input_source and should be unreferenced with g_object_unref() when
+ * finished with it.
+ *
+ * If @input_source does NOT have an #GDBusObject (implying it's a
+ * throw-away template #ESource), its contents are submitted to the D-Bus
+ * service through e_source_registry_create_source_sync().  If the operation
+ * is successful and @output_source is non-%NULL, @output_source will be set
+ * to a new #ESource created from @input_source and should be unreferenced
+ * with g_object_unref() when finished with it.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_commit_source_sync (ESourceRegistry *registry,
+                                      ESource *input_source,
+                                      ESource **output_source,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+	GDBusObject *dbus_object;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (input_source), FALSE);
+
+	dbus_object = e_source_ref_dbus_object (input_source);
+
+	if (dbus_object != NULL) {
+		success = e_source_write_sync (
+			input_source, cancellable, error);
+		if (success && output_source != NULL)
+			*output_source = g_object_ref (input_source);
+		g_object_unref (dbus_object);
+	} else {
+		success = e_source_registry_create_source_sync (
+			registry, input_source, output_source,
+			cancellable, error);
+	}
+
+	return success;
+}
+
+/**
+ * e_source_registry_commit_source:
+ * @registry: an #ESourceRegistry
+ * @input_source: an #ESource with changes to commit
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * See e_source_registry_commit_source_sync() for details.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_registry_commit_source_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_commit_source (ESourceRegistry *registry,
+                                 ESource *input_source,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (E_IS_SOURCE (input_source));
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->input_source = g_object_ref (input_source);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (registry), callback, user_data,
+		e_source_registry_commit_source);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_registry_commit_source_thread,
+		G_PRIORITY_DEFAULT, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_registry_commit_source_finish:
+ * @registry: an #ESourceRegistry
+ * @result: a #GAsyncResult
+ * @output_source: return location for the committed #ESource, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_registry_commit_source().
+ *
+ * If the operation is successful, the #ESource returned through
+ * @output_source should be unreferenced with g_object_unref() when
+ * finished with it.
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_commit_source_finish (ESourceRegistry *registry,
+                                        GAsyncResult *result,
+                                        ESource **output_source,
+                                        GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (registry),
+		e_source_registry_commit_source), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+
+	if (output_source != NULL)
+		*output_source = g_object_ref (async_context->output_source);
+
+	return TRUE;
+}
+
+/* Helper for e_source_registry_create_source() */
+static void
+source_registry_create_source_thread (GSimpleAsyncResult *simple,
+                                      GObject *object,
+                                      GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_registry_create_source_sync (
+		E_SOURCE_REGISTRY (object),
+		async_context->input_source,
+		&async_context->output_source,
+		cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/* Helper for e_source_registry_create_source_sync() */
+static gboolean
+source_registry_create_idle_cb (gpointer user_data)
+{
+	CreateContext *create_context = user_data;
+
+	/* We should know what object path we're looking for by now. */
+	g_return_val_if_fail (create_context->object_path != NULL, FALSE);
+
+	/* Find the corresponding ESource in the object path table.
+	 * Note that the lookup returns a new ESource reference. */
+	create_context->source =
+		source_registry_object_path_table_lookup (
+		create_context->registry, create_context->object_path);
+
+	/* If we got a hit, terminate the main loop. */
+	if (create_context->source != NULL)
+		g_main_loop_quit (create_context->main_loop);
+
+	return FALSE;
+}
+
+/* Helper for e_source_registry_create_source_sync() */
+static void
+source_registry_create_object_added_cb (GDBusObjectManager *object_manager,
+                                        GDBusObject *dbus_object,
+                                        CreateContext *create_context)
+{
+	GSource *idle_source;
+
+	/* Schedule a callback in response to an object being added.
+	 * Note that we're in the object manager thread here, so we
+	 * want to do as little as possible. */
+
+	idle_source = g_idle_source_new ();
+	g_source_set_callback (
+		idle_source,
+		source_registry_create_idle_cb,
+		create_context, (GDestroyNotify) NULL);
+	g_source_attach (idle_source, create_context->main_context);
+	g_source_unref (idle_source);
+}
+
+/**
+ * e_source_registry_create_source_sync:
+ * @registry: an #ESourceRegistry
+ * @input_source: an #ESource with no #GDBusObject
+ * @output_source: return location for a newly-created #ESource, or %NULL
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Requests the D-Bus service to create a new key file from @input_source
+ * and broadcast its addition to all clients.  The @input_source must be a
+ * throw-away #ESource with no #GDBusObject.
+ *
+ * If the operation is successful and @output_source is non-%NULL,
+ * @output_source will be set to a newly-created #ESource instance, which
+ * should be unreferenced with g_object_unref() when finished with it.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_create_source_sync (ESourceRegistry *registry,
+                                      ESource *input_source,
+                                      ESource **output_source,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+	CreateContext *create_context;
+	gulong object_added_id;
+	gchar *source_data;
+	const gchar *uid;
+	gboolean success;
+
+	/* We have to synchronize with the object manager thread to
+	 * obtain the newly-created ESource.  This function was the
+	 * reason I gave the object manager its own thread. */
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (input_source), FALSE);
+
+	/* XXX It might be wise to return an error if the input_source's
+	 *     parent UID is not already present in the registry, else we
+	 *     risk deadlocking if the newly-added source winds up in the
+	 *     orphan table on the server-side and never gets exported.
+	 *
+	 *     This is most likely to occur when creating a collection of
+	 *     related sources one at a time and the caller is not careful
+	 *     to create parent sources before children.  Perhaps a method
+	 *     to create the collection in parallel would help to mitigate
+	 *     the deadlock risk?
+	 */
+
+	create_context = create_context_new (registry);
+
+	/* Start listening for the "object-added" signal BEFORE we call
+	 * the D-Bus method, even though we don't know what object path
+	 * we're looking for yet.  The signal may be emitted before the
+	 * D-Bus method returns. */
+	object_added_id = g_signal_connect_data (
+		registry->priv->dbus_object_manager, "object-added",
+		G_CALLBACK (source_registry_create_object_added_cb),
+		create_context, (GClosureNotify) create_context_free, 0);
+
+	uid = e_source_get_uid (input_source);
+	source_data = e_source_to_string (input_source, NULL);
+
+	/* Setting the object path directly on the CreateContext
+	 * is thread-safe because only the idle callbacks on our
+	 * GMainContext use the object path, but we have not yet
+	 * started our main loop so any scheduled idle callbacks
+	 * are still pending. */
+	success = e_dbus_source_manager_call_create_source_sync (
+		registry->priv->dbus_source_manager,
+		uid, source_data, &create_context->object_path,
+		cancellable, error);
+
+	g_free (source_data);
+
+	/* Sanity check. */
+	g_warn_if_fail (
+		(success && create_context->object_path != NULL) ||
+		(!success && create_context->object_path == NULL));
+
+	if (success) {
+		/* Now start the main loop.  It will terminate
+		 * when we have our newly-created ESource. */
+		g_main_loop_run (create_context->main_loop);
+
+		g_warn_if_fail (E_IS_SOURCE (create_context->source));
+
+		/* Hand the newly-created ESource back to
+		 * the caller if the caller is interested. */
+		if (output_source != NULL) {
+			*output_source = create_context->source;
+			create_context->source = NULL;
+		}
+	}
+
+	/* Clean up. */
+
+	/* This will free the CreateContext signal handler data. */
+	g_signal_handler_disconnect (
+		registry->priv->dbus_object_manager, object_added_id);
+
+	return success;
+}
+
+/**
+ * e_source_registry_create_source:
+ * @registry: an #ESourceRegistry
+ * @input_source: an #ESource with no #EBusObject
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * See e_source_registry_create_source_sync() for details.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_registry_create_source_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_create_source (ESourceRegistry *registry,
+                                 ESource *input_source,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (E_IS_SOURCE (input_source));
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->input_source = g_object_ref (input_source);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (registry), callback, user_data,
+		e_source_registry_create_source);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_registry_create_source_thread,
+		G_PRIORITY_DEFAULT, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_registry_create_source_finish:
+ * @registry: an #ESourceRegistry
+ * @result: a #GAsyncResult
+ * @output_source: return location for a newly-created #ESource, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_registry_create_source().
+ *
+ * If the operation is successful, the #ESource returned through
+ * @output_source should be unreferenced with g_object_unref() when
+ * finished with it.
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_create_source_finish (ESourceRegistry *registry,
+                                        GAsyncResult *result,
+                                        ESource **output_source,
+                                        GError **error)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (registry),
+		e_source_registry_create_source), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+
+	if (output_source != NULL)
+		*output_source = g_object_ref (async_context->output_source);
+
+	return TRUE;
+}
+
+/* Helper for e_source_registry_create_multiple_sources() */
+static void
+source_registry_create_multiple_sources_thread (GSimpleAsyncResult *simple,
+                                                GObject *object,
+                                                GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_registry_create_multiple_sources_sync (
+		E_SOURCE_REGISTRY (object),
+		async_context->input_sources,
+		cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/**
+ * e_source_registry_create_multiple_sources_sync:
+ * @registry: an #ESourceRegistry
+ * @input_sources: a list of #ESource instances with no #GDBusObject
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Requests the D-Bus service create new key files for each #ESource in
+ * @input_sources.  Each list element must be a throw-away #ESource with
+ * no #GDBusObject.
+ *
+ * Use this function when creating a collection of related data sources
+ * so the D-Bus service can handle them all at once.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_create_multiple_sources_sync (ESourceRegistry *registry,
+                                                GList *input_sources,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+	GVariantBuilder builder;
+	GVariant *variant;
+	GList *link;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
+
+	/* Verify the list elements are all ESources. */
+	for (link = input_sources; link != NULL; link = g_list_next (link))
+		g_return_val_if_fail (E_IS_SOURCE (link->data), FALSE);
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+
+	for (link = input_sources; link != NULL; link = g_list_next (link)) {
+		ESource *source;
+		const gchar *uid;
+		gchar *source_data;
+
+		source = E_SOURCE (link->data);
+		uid = e_source_get_uid (source);
+
+		source_data = e_source_to_string (source, NULL);
+		g_variant_builder_add (&builder, "{ss}", uid, source_data);
+		g_free (source_data);
+	}
+
+	variant = g_variant_builder_end (&builder);
+
+	/* This function sinks the floating GVariant reference. */
+	success = e_dbus_source_manager_call_create_sources_sync (
+		registry->priv->dbus_source_manager,
+		variant, cancellable, error);
+
+	g_variant_builder_clear (&builder);
+
+	return success;
+}
+
+/**
+ * e_source_registry_create_multiple_sources:
+ * @registry: an #ESourceRegistry
+ * @input_sources: a list of #ESource instances with no #GDBusObject
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request
+ *            is satisfied
+ * @user_data: (closure): data to pass to the callback function
+ *
+ * Asynchronously requests the D-Bus service create new key files for each
+ * #ESource in @input_sources.  Each list element must be a throw-away
+ * #ESource with no #GDBusObject.
+ *
+ * Use this function when creating a collection of related data sources
+ * so the D-Bus service can handle them all at once.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_registry_create_multiple_sources_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_create_multiple_sources (ESourceRegistry *registry,
+                                           GList *input_sources,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+	GList *link;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	/* Verify the list elements are all ESources. */
+	for (link = input_sources; link != NULL; link = g_list_next (link))
+		g_return_if_fail (E_IS_SOURCE (link->data));
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->input_sources = g_list_copy (input_sources);
+
+	g_list_foreach (
+		async_context->input_sources,
+		(GFunc) g_object_ref, NULL);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (registry), callback, user_data,
+		e_source_registry_create_multiple_sources);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_registry_create_multiple_sources_thread,
+		G_PRIORITY_DEFAULT, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_registry_create_multiple_sources_finish:
+ * @registry: an #ESourceRegistry
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with
+ * e_source_registry_create_multiple_sources().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_create_multiple_sources_finish (ESourceRegistry *registry,
+                                                  GAsyncResult *result,
+                                                  GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (registry),
+		e_source_registry_create_multiple_sources), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_source_registry_ref_source:
+ * @registry: an #ESourceRegistry
+ * @uid: a unique identifier string
+ *
+ * Looks up an #ESource in @registry by its unique identifier string.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: an #ESource, or %NULL if no match was found
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_source (ESourceRegistry *registry,
+                              const gchar *uid)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	return source_registry_sources_lookup (registry, uid);
+}
+
+/**
+ * e_source_registry_list_sources:
+ * @registry: an #ESourceRegistry
+ * @extension_name: an extension name, or %NULL
+ *
+ * Returns a list of registered sources, sorted by display name.  If
+ * @extension_name is given, restrict the list to sources having that
+ * extension name.
+ *
+ * The sources returned in the list are referenced for thread-safety.
+ * They must each be unreferenced with g_object_unref() when finished
+ * when them.  Free the returned list itself with g_list_free().
+ *
+ * An easy way to free the list properly in one step is as follows:
+ *
+ * |[
+ *   g_list_free_full (list, g_object_unref);
+ * ]|
+ *
+ * Returns: a sorted list of sources
+ *
+ * Since: 3.6
+ **/
+GList *
+e_source_registry_list_sources (ESourceRegistry *registry,
+                                const gchar *extension_name)
+{
+	GList *list, *link;
+	GQueue trash = G_QUEUE_INIT;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	list = g_list_sort (
+		source_registry_sources_get_values (registry),
+		(GCompareFunc) e_source_compare_by_display_name);
+
+	if (extension_name == NULL)
+		return list;
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		ESource *source = E_SOURCE (link->data);
+
+		if (!e_source_has_extension (source, extension_name)) {
+			g_queue_push_tail (&trash, link);
+			g_object_unref (source);
+		}
+	}
+
+	/* We do want pop_head() here, not pop_head_link(). */
+	while ((link = g_queue_pop_head (&trash)) != NULL)
+		list = g_list_delete_link (list, link);
+
+	return list;
+}
+
+/**
+ * e_source_registry_find_extension:
+ * @registry: an #ESourceRegistry
+ * @source: an #ESource
+ * @extension_name: the extension name to find
+ *
+ * Examines @source and its ancestors and returns the "deepest" #ESource
+ * having an #ESourceExtension with the given @extension_name.  If neither
+ * @source nor any of its ancestors have such an extension, the function
+ * returns %NULL.
+ *
+ * This function is useful in cases when an #ESourceExtension is meant to
+ * apply to both the #ESource it belongs to and the #ESource's descendants.
+ *
+ * A common example is the #ESourceCollection extension, where descendants
+ * of an #ESource having an #ESourceCollection extension are implied to be
+ * members of that collection.  In that example, this function can be used
+ * to test whether @source is a member of a collection.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Note the function returns the #ESource containing the #ESourceExtension
+ * instead of the #ESourceExtension itself because extension instances are
+ * not to be referenced directly (see e_source_get_extension()).
+ *
+ * Returns: an #ESource, or %NULL if no match was found
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_find_extension (ESourceRegistry *registry,
+                                  ESource *source,
+                                  const gchar *extension_name)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	g_return_val_if_fail (extension_name != NULL, NULL);
+
+	g_object_ref (source);
+
+	while (!e_source_has_extension (source, extension_name)) {
+		gchar *uid;
+
+		uid = e_source_dup_parent (source);
+
+		g_object_unref (source);
+		source = NULL;
+
+		if (uid != NULL) {
+			source = e_source_registry_ref_source (registry, uid);
+			g_free (uid);
+		}
+
+		if (source == NULL)
+			break;
+	}
+
+	return source;
+}
+
+/* Helper for e_source_registry_build_display_tree() */
+static gint
+source_registry_compare_nodes (GNode *node_a,
+                               GNode *node_b)
+{
+	ESource *source_a = E_SOURCE (node_a->data);
+	ESource *source_b = E_SOURCE (node_b->data);
+	const gchar *uid_a, *uid_b;
+
+	uid_a = e_source_get_uid (source_a);
+	uid_b = e_source_get_uid (source_b);
+
+	/* Sanity check, with runtime warnings. */
+	if (uid_a == NULL) {
+		g_warn_if_reached ();
+		uid_a = "";
+	}
+	if (uid_b == NULL) {
+		g_warn_if_reached ();
+		uid_b = "";
+	}
+
+	/* The built-in "local-stub" source comes first at depth 1. */
+
+	if (g_strcmp0 (uid_a, "local-stub") == 0)
+		return -1;
+
+	if (g_strcmp0 (uid_b, "local-stub") == 0)
+		return 1;
+
+	/* The built-in "system-*" sources come first at depth 2. */
+
+	if (g_str_has_prefix (uid_a, "system-"))
+		return -1;
+
+	if (g_str_has_prefix (uid_b, "system-"))
+		return 1;
+
+	return e_source_compare_by_display_name (source_a, source_b);
+}
+
+/* Helper for e_source_registry_build_display_tree() */
+static gboolean
+source_registry_prune_nodes (GNode *node,
+                             const gchar *extension_name)
+{
+	GQueue queue = G_QUEUE_INIT;
+	GNode *child;
+
+	/* Unlink all the child nodes and place them in a queue. */
+	while ((child = g_node_first_child (node)) != NULL) {
+		g_node_unlink (child);
+		g_queue_push_tail (&queue, child);
+	}
+
+	/* Sort the queue by source name. */
+	g_queue_sort (
+		&queue, (GCompareDataFunc)
+		source_registry_compare_nodes, NULL);
+
+	/* Pop nodes off the head of the queue until the queue is empty.
+	 * If the node has either its own children or the given extension
+	 * name, put it back under the parent node (preserving the sorted
+	 * order).  Otherwise delete the node. */
+	while ((child = g_queue_pop_head (&queue)) != NULL) {
+		ESource *source = E_SOURCE (child->data);
+
+		if (extension_name == NULL) {
+			if (e_source_get_enabled (source)) {
+				g_node_append (node, child);
+			} else {
+				g_object_unref (source);
+				g_node_destroy (child);
+			}
+
+		} else if (e_source_has_extension (source, extension_name)) {
+			if (e_source_get_enabled (source)) {
+				g_node_append (node, child);
+			} else {
+				g_object_unref (source);
+				g_node_destroy (child);
+			}
+
+		} else if (g_node_first_child (child) != NULL) {
+			g_node_append (node, child);
+
+		} else {
+			g_object_unref (source);
+			g_node_destroy (child);
+		}
+	}
+
+	return FALSE;
+}
+
+/**
+ * e_source_registry_build_display_tree:
+ * @registry: an #ESourceRegistry
+ * @extension_name: an extension name, or %NULL
+ *
+ * Returns a single #GNode tree of registered sources that can be used to
+ * populate a #GtkTreeModel.  (The root #GNode is just an empty placeholder.)
+ *
+ * Similar to e_source_registry_list_sources(), an @extension_name can be
+ * given to restrict the tree to sources having that extension name.  Parents
+ * of matched sources are included in the tree regardless of whether they have
+ * an extension named @extension_name.
+ *
+ * Disabled leaf nodes are automatically excluded from the #GNode tree.
+ *
+ * The sources returned in the tree are referenced for thread-safety.
+ * They must each be unreferenced with g_object_unref() when finished
+ * with them.  Free the returned tree itself with g_node_destroy().
+ * For convenience, e_source_registry_free_display_tree() does all
+ * that in one step.
+ *
+ * Returns: a tree of sources, arranged for display
+ *
+ * Since: 3.6
+ **/
+GNode *
+e_source_registry_build_display_tree (ESourceRegistry *registry,
+                                      const gchar *extension_name)
+{
+	GNode *root;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	/* Assemble all data sources into a tree. */
+	root = source_registry_sources_build_tree (registry);
+
+	/* Prune unwanted nodes from the copied source trees.
+	 * This must be done in "post" order (children first)
+	 * since it reorders and deletes child nodes. */
+	g_node_traverse (
+		root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
+		(GNodeTraverseFunc) source_registry_prune_nodes,
+		(gpointer) extension_name);
+
+	return root;
+}
+
+/* Helper for e_source_registry_free_display_tree() */
+static void
+source_registry_unref_nodes (GNode *node)
+{
+	while (node != NULL) {
+		if (node->children != NULL)
+			source_registry_unref_nodes (node->children);
+		if (node->data != NULL)
+			g_object_unref (node->data);
+		node = node->next;
+	}
+}
+
+/**
+ * e_source_registry_free_display_tree:
+ * @display_tree: a tree of sources, arranged for display
+ *
+ * Convenience function to free a #GNode tree of registered
+ * sources created by e_source_registry_build_display_tree().
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_free_display_tree (GNode *display_tree)
+{
+	g_return_if_fail (display_tree != NULL);
+
+	/* XXX This would be easier if GLib had something like
+	 *     g_node_destroy_full() which took a GDestroyNotify.
+	 *     Then the tree would not have to be traversed twice. */
+
+	source_registry_unref_nodes (display_tree);
+	g_node_destroy (display_tree);
+}
+
+/* Helper for e_source_registry_debug_dump() */
+static gboolean
+source_registry_debug_dump_cb (GNode *node)
+{
+	guint ii, depth;
+
+	/* Root node is an empty placeholder. */
+	if (G_NODE_IS_ROOT (node))
+		return FALSE;
+
+	depth = g_node_depth (node);
+	for (ii = 2; ii < depth; ii++)
+		g_print ("    ");
+
+	if (E_IS_SOURCE (node->data)) {
+		ESource *source = E_SOURCE (node->data);
+		g_print ("\"%s\" ", e_source_get_display_name (source));
+		g_print ("(%s)", e_source_get_uid (source));
+	}
+
+	g_print ("\n");
+
+	return FALSE;
+}
+
+/**
+ * e_source_registry_debug_dump:
+ * @registry: an #ESourceRegistry
+ * @extension_name: an extension name, or %NULL
+ *
+ * Handy debugging function that uses e_source_registry_build_display_tree()
+ * to print a tree of registered sources to standard output.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_debug_dump (ESourceRegistry *registry,
+                              const gchar *extension_name)
+{
+	GNode *root;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	root = e_source_registry_build_display_tree (registry, extension_name);
+
+	g_node_traverse (
+		root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+		(GNodeTraverseFunc) source_registry_debug_dump_cb, NULL);
+
+	e_source_registry_free_display_tree (root);
+}
+
+/**
+ * e_source_registry_ref_builtin_address_book:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the built-in address book #ESource.
+ *
+ * This #ESource is always present and makes for a safe fallback.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the built-in address book #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_builtin_address_book (ESourceRegistry *registry)
+{
+	ESource *source;
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	uid = E_SOURCE_BUILTIN_ADDRESS_BOOK_UID;
+	source = e_source_registry_ref_source (registry, uid);
+	g_return_val_if_fail (source != NULL, NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_ref_default_address_book:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the #ESource most recently passed to
+ * e_source_registry_set_default_address_book() either in this session
+ * or a previous session, or else falls back to the built-in address book.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the default address book #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_default_address_book (ESourceRegistry *registry)
+{
+	const gchar *key;
+	ESource *source;
+	gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	key = E_SETTINGS_DEFAULT_ADDRESS_BOOK_KEY;
+	uid = g_settings_get_string (registry->priv->settings, key);
+	source = e_source_registry_ref_source (registry, uid);
+	g_free (uid);
+
+	/* The built-in source is always present. */
+	if (source == NULL)
+		source = e_source_registry_ref_builtin_address_book (registry);
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_set_default_address_book:
+ * @registry: an #ESourceRegistry
+ * @default_source: an address book #ESource, or %NULL
+ *
+ * Sets @default_source as the default address book.  If @default_source
+ * is %NULL, the default address book is reset to the built-in address book.
+ * This setting will persist across sessions until changed.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_set_default_address_book (ESourceRegistry *registry,
+                                            ESource *default_source)
+{
+	const gchar *key;
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	if (default_source != NULL) {
+		g_return_if_fail (E_IS_SOURCE (default_source));
+		uid = e_source_get_uid (default_source);
+	} else {
+		uid = E_SOURCE_BUILTIN_ADDRESS_BOOK_UID;
+	}
+
+	key = E_SETTINGS_DEFAULT_ADDRESS_BOOK_KEY;
+	g_settings_set_string (registry->priv->settings, key, uid);
+
+	/* The GSettings::changed signal will trigger a "notify" signal
+	 * from the registry, so no need to call g_object_notify() here. */
+}
+
+/**
+ * e_source_registry_ref_builtin_calendar:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the built-in calendar #ESource.
+ *
+ * This #ESource is always present and makes for a safe fallback.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the built-in calendar #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_builtin_calendar (ESourceRegistry *registry)
+{
+	ESource *source;
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	uid = E_SOURCE_BUILTIN_CALENDAR_UID;
+	source = e_source_registry_ref_source (registry, uid);
+	g_return_val_if_fail (source != NULL, NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_ref_default_calendar:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the #ESource most recently passed to
+ * e_source_registry_set_default_calendar() either in this session
+ * or a previous session, or else falls back to the built-in calendar.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the default calendar #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_default_calendar (ESourceRegistry *registry)
+{
+	const gchar *key;
+	ESource *source;
+	gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	key = E_SETTINGS_DEFAULT_CALENDAR_KEY;
+	uid = g_settings_get_string (registry->priv->settings, key);
+	source = e_source_registry_ref_source (registry, uid);
+	g_free (uid);
+
+	/* The built-in source is always present. */
+	if (source == NULL)
+		source = e_source_registry_ref_builtin_calendar (registry);
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_set_default_calendar:
+ * @registry: an #ESourceRegistry
+ * @default_source: a calendar #ESource, or %NULL
+ *
+ * Sets @default_source as the default calendar.  If @default_source
+ * is %NULL, the default calendar is reset to the built-in calendar.
+ * This setting will persist across sessions until changed.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_set_default_calendar (ESourceRegistry *registry,
+                                        ESource *default_source)
+{
+	const gchar *key;
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	if (default_source != NULL) {
+		g_return_if_fail (E_IS_SOURCE (default_source));
+		uid = e_source_get_uid (default_source);
+	} else {
+		uid = E_SOURCE_BUILTIN_CALENDAR_UID;
+	}
+
+	key = E_SETTINGS_DEFAULT_CALENDAR_KEY;
+	g_settings_set_string (registry->priv->settings, key, uid);
+
+	/* The GSettings::changed signal will trigger a "notify" signal
+	 * from the registry, so no need to call g_object_notify() here. */
+}
+
+/**
+ * e_source_registry_ref_builtin_mail_account:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the built-in mail account #ESource.
+ *
+ * This #ESource is always present and makes for a safe fallback.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the built-in mail account #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_builtin_mail_account (ESourceRegistry *registry)
+{
+	ESource *source;
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	uid = E_SOURCE_BUILTIN_MAIL_ACCOUNT_UID;
+	source = e_source_registry_ref_source (registry, uid);
+	g_return_val_if_fail (source != NULL, NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_ref_default_mail_account:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the #ESource most recently passed to
+ * e_source_registry_set_default_mail_account() either in this session
+ * or a previous session, or else falls back to the built-in mail account.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the default mail account #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_default_mail_account (ESourceRegistry *registry)
+{
+	const gchar *key;
+	ESource *source;
+	gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	key = E_SETTINGS_DEFAULT_MAIL_ACCOUNT_KEY;
+	uid = g_settings_get_string (registry->priv->settings, key);
+	source = e_source_registry_ref_source (registry, uid);
+	g_free (uid);
+
+	/* The built-in source is always present. */
+	if (source == NULL)
+		source = e_source_registry_ref_builtin_mail_account (registry);
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_set_default_mail_account:
+ * @registry: an #ESourceRegistry
+ * @default_source: a mail account #ESource, or %NULL
+ *
+ * Sets @default_source as the default mail account.  If @default_source
+ * is %NULL, the default mail account is reset to the built-in mail account.
+ * This setting will persist across sessions until changed.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_set_default_mail_account (ESourceRegistry *registry,
+                                            ESource *default_source)
+{
+	const gchar *key;
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	if (default_source != NULL) {
+		g_return_if_fail (E_IS_SOURCE (default_source));
+		uid = e_source_get_uid (default_source);
+	} else {
+		uid = E_SOURCE_BUILTIN_MAIL_ACCOUNT_UID;
+	}
+
+	key = E_SETTINGS_DEFAULT_MAIL_ACCOUNT_KEY;
+	g_settings_set_string (registry->priv->settings, key, uid);
+
+	/* The GSettings::changed signal will trigger a "notify" signal
+	 * from the registry, so no need to call g_object_notify() here. */
+}
+
+/* Helper for e_source_registry_ref_default_mail_identity() */
+static ESource *
+source_registry_ref_any_mail_identity (ESourceRegistry *registry)
+{
+	ESource *source;
+	GList *list, *link;
+	const gchar *extension_name;
+	gchar *uid = NULL;
+
+	/* First fallback: Return the mail identity named
+	 *                 by the default mail account. */
+
+	source = e_source_registry_ref_default_mail_account (registry);
+
+	/* This should never be NULL, but just to be safe. */
+	if (source != NULL) {
+		ESourceMailAccount *extension;
+
+		extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+		extension = e_source_get_extension (source, extension_name);
+		uid = e_source_mail_account_dup_identity_uid (extension);
+
+		g_object_unref (source);
+		source = NULL;
+	}
+
+	if (uid != NULL) {
+		source = e_source_registry_ref_source (registry, uid);
+		g_free (uid);
+	}
+
+	if (source != NULL)
+		return source;
+
+	/* Second fallback: Pick any available mail identity,
+	 *                  preferring enabled identities. */
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+	list = e_source_registry_list_sources (registry, extension_name);
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		ESource *candidate = E_SOURCE (link->data);
+
+		if (e_source_get_enabled (candidate)) {
+			source = g_object_ref (candidate);
+			break;
+		}
+	}
+
+	if (source == NULL && list != NULL)
+		source = g_object_ref (link->data);
+
+	g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+	return source;
+}
+
+/**
+ * e_source_registry_ref_default_mail_identity:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the #ESource most recently passed to
+ * e_source_registry_set_default_mail_identity() either in this session
+ * or a previous session, or else falls back to the mail identity named
+ * by the default mail account.  If even that fails it returns any mail
+ * identity from @registry, or %NULL if there are none.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: (transfer full): the default mail identity #ESource, or %NULL
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_default_mail_identity (ESourceRegistry *registry)
+{
+	const gchar *key;
+	ESource *source;
+	gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	key = E_SETTINGS_DEFAULT_MAIL_IDENTITY_KEY;
+	uid = g_settings_get_string (registry->priv->settings, key);
+	source = e_source_registry_ref_source (registry, uid);
+	g_free (uid);
+
+	if (source == NULL)
+		source = source_registry_ref_any_mail_identity (registry);
+
+	return source;
+}
+
+/**
+ * e_source_registry_set_default_mail_identity:
+ * @registry: an #ESourceRegistry
+ * @default_source: (allow-none): a mail identity #ESource, or %NULL
+ *
+ * Sets @default_source as the default mail identity.  If @default_source
+ * is %NULL, the next request for the default mail identity will use the
+ * fallbacks described in e_source_registry_get_default_mail_identity().
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_set_default_mail_identity (ESourceRegistry *registry,
+                                             ESource *default_source)
+{
+	const gchar *key;
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	if (default_source != NULL) {
+		g_return_if_fail (E_IS_SOURCE (default_source));
+		uid = e_source_get_uid (default_source);
+	} else {
+		uid = "";  /* no built-in mail identity */
+	}
+
+	key = E_SETTINGS_DEFAULT_MAIL_IDENTITY_KEY;
+	g_settings_set_string (registry->priv->settings, key, uid);
+
+	/* The GSettings::changed signal will trigger a "notify" signal
+	 * from the registry, so no need to call g_object_notify() here. */
+}
+
+/**
+ * e_source_registry_ref_builtin_memo_list:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the built-in memo list #ESource.
+ *
+ * This #ESource is always present and makes for a safe fallback.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the built-in memo list #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_builtin_memo_list (ESourceRegistry *registry)
+{
+	ESource *source;
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	uid = E_SOURCE_BUILTIN_MEMO_LIST_UID;
+	source = e_source_registry_ref_source (registry, uid);
+	g_return_val_if_fail (source != NULL, NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_ref_default_memo_list:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the #ESource most recently passed to
+ * e_source_registry_set_default_memo_list() either in this session
+ * or a previous session, or else falls back to the built-in memo list.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the default memo list #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_default_memo_list (ESourceRegistry *registry)
+{
+	const gchar *key;
+	ESource *source;
+	gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	key = E_SETTINGS_DEFAULT_MEMO_LIST_KEY;
+	uid = g_settings_get_string (registry->priv->settings, key);
+	source = e_source_registry_ref_source (registry, uid);
+	g_free (uid);
+
+	/* The built-in source is always present. */
+	if (source == NULL)
+		source = e_source_registry_ref_builtin_memo_list (registry);
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_set_default_memo_list:
+ * @registry: an #ESourceRegistry
+ * @default_source: a memo list #ESource, or %NULL
+ *
+ * Sets @default_source as the default memo list.  If @default_source
+ * is %NULL, the default memo list is reset to the built-in memo list.
+ * This setting will persist across sessions until changed.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_set_default_memo_list (ESourceRegistry *registry,
+                                         ESource *default_source)
+{
+	const gchar *key;
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	if (default_source != NULL) {
+		g_return_if_fail (E_IS_SOURCE (default_source));
+		uid = e_source_get_uid (default_source);
+	} else {
+		uid = E_SOURCE_BUILTIN_MEMO_LIST_UID;
+	}
+
+	key = E_SETTINGS_DEFAULT_MEMO_LIST_KEY;
+	g_settings_set_string (registry->priv->settings, key, uid);
+
+	/* The GSettings::changed signal will trigger a "notify" signal
+	 * from the registry, so no need to call g_object_notify() here. */
+}
+
+/**
+ * e_source_registry_ref_builtin_task_list:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the built-in task list #ESource.
+ *
+ * This #ESource is always present and makes for a safe fallback.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the built-in task list #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_builtin_task_list (ESourceRegistry *registry)
+{
+	ESource *source;
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	uid = E_SOURCE_BUILTIN_TASK_LIST_UID;
+	source = e_source_registry_ref_source (registry, uid);
+	g_return_val_if_fail (source != NULL, NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_ref_default_task_list:
+ * @registry: an #ESourceRegistry
+ *
+ * Returns the #ESource most recently passed to
+ * e_source_registry_set_default_task_list() either in this session
+ * or a previous session, or else falls back to the built-in task list.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the default task list #ESource
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_default_task_list (ESourceRegistry *registry)
+{
+	const gchar *key;
+	ESource *source;
+	gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	key = E_SETTINGS_DEFAULT_TASK_LIST_KEY;
+	uid = g_settings_get_string (registry->priv->settings, key);
+	source = e_source_registry_ref_source (registry, uid);
+	g_free (uid);
+
+	/* The built-in source is always present. */
+	if (source == NULL)
+		source = e_source_registry_ref_builtin_task_list (registry);
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	return source;
+}
+
+/**
+ * e_source_registry_set_default_task_list:
+ * @registry: an #ESourceRegistry
+ * @default_source: a task list #ESource, or %NULL
+ *
+ * Sets @default_source as the default task list.  If @default_source
+ * is %NULL, the default task list is reset to the built-in task list.
+ * This setting will persist across sessions until changed.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_set_default_task_list (ESourceRegistry *registry,
+                                         ESource *default_source)
+{
+	const gchar *key;
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	if (default_source != NULL) {
+		g_return_if_fail (E_IS_SOURCE (default_source));
+		uid = e_source_get_uid (default_source);
+	} else {
+		uid = E_SOURCE_BUILTIN_TASK_LIST_UID;
+	}
+
+	key = E_SETTINGS_DEFAULT_TASK_LIST_KEY;
+	g_settings_set_string (registry->priv->settings, key, uid);
+
+	/* The GSettings::changed signal will trigger a "notify" signal
+	 * from the registry, so no need to call g_object_notify() here. */
+}
+
+/**
+ * e_source_registry_ref_default_for_extension_name:
+ * @registry: an #ESourceRegistry
+ * @extension_name: an extension_name
+ *
+ * This is a convenience function to return a default #ESource based on
+ * @extension_name.  This only works with a subset of extension names.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_ADDRESS_BOOK, the function
+ * returns the current default address book, or else falls back to the
+ * built-in address book.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_CALENDAR, the function returns
+ * the current default calendar, or else falls back to the built-in calendar.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_MAIL_ACCOUNT, the function
+ * returns the current default mail account, or else falls back to the
+ * built-in mail account.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_MAIL_IDENTITY, the function
+ * returns the current default mail identity, or else falls back to the
+ * mail identity named by the current default mail account.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_MEMO_LIST, the function returns
+ * the current default memo list, or else falls back to the built-in memo list.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_TASK_LIST, the function returns
+ * the current default task list, or else falls back to the built-in task list.
+ *
+ * For all other values of @extension_name, the function returns %NULL.
+ *
+ * The returned #ESource is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: the default #ESource based on @extension_name
+ *
+ * Since: 3.6
+ **/
+ESource *
+e_source_registry_ref_default_for_extension_name (ESourceRegistry *registry,
+                                                  const gchar *extension_name)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+	g_return_val_if_fail (extension_name != NULL, NULL);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_ADDRESS_BOOK) == 0)
+		return e_source_registry_ref_default_address_book (registry);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0)
+		return e_source_registry_ref_default_calendar (registry);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_ACCOUNT) == 0)
+		return e_source_registry_ref_default_mail_account (registry);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_IDENTITY) == 0)
+		return e_source_registry_ref_default_mail_identity (registry);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0)
+		return e_source_registry_ref_default_memo_list (registry);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_TASK_LIST) == 0)
+		return e_source_registry_ref_default_task_list (registry);
+
+	return NULL;
+}
+
+/**
+ * e_source_registry_set_default_for_extension_name:
+ * @registry: an #ESourceRegistry
+ * @extension_name: an extension name
+ * @default_source: an #ESource, or %NULL
+ *
+ * This is a convenience function to set a default #ESource based on
+ * @extension_name.  This only works with a subset of extension names.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_ADDRESS_BOOK, the function
+ * sets @default_source as the default address book.  If @default_source
+ * is %NULL, the default address book is reset to the built-in address book.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_CALENDAR, the function sets
+ * @default_source as the default calendar.  If @default_source is %NULL,
+ * the default calendar is reset to the built-in calendar.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_MAIL_ACCOUNT, the function
+ * sets @default_source as the default mail account.  If @default_source
+ * is %NULL, the default mail account is reset to the built-in mail account.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_MAIL_IDENTITY, the function
+ * sets @default_source as the default mail identity.  If @default_source
+ * is %NULL, the next request for the default mail identity will return
+ * the mail identity named by the default mail account.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_MEMO_LIST, the function sets
+ * @default_source as the default memo list.  If @default_source is %NULL,
+ * the default memo list is reset to the built-in memo list.
+ *
+ * If @extension_name is #E_SOURCE_EXTENSION_TASK_LIST, the function sets
+ * @default_source as the default task list.  If @default_source is %NULL,
+ * the default task list is reset to the built-in task list.
+ *
+ * For all other values of @extension_name, the function does nothing.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_set_default_for_extension_name (ESourceRegistry *registry,
+                                                  const gchar *extension_name,
+                                                  ESource *default_source)
+{
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (extension_name != NULL);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_ADDRESS_BOOK) == 0)
+		e_source_registry_set_default_address_book (
+			registry, default_source);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0)
+		e_source_registry_set_default_calendar (
+			registry, default_source);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_ACCOUNT) == 0)
+		e_source_registry_set_default_mail_account (
+			registry, default_source);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_IDENTITY) == 0)
+		e_source_registry_set_default_mail_identity (
+			registry, default_source);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0)
+		e_source_registry_set_default_memo_list (
+			registry, default_source);
+
+	if (strcmp (extension_name, E_SOURCE_EXTENSION_TASK_LIST) == 0)
+		e_source_registry_set_default_task_list (
+			registry, default_source);
+}
+
diff --git a/libedataserver/e-source-registry.h b/libedataserver/e-source-registry.h
new file mode 100644
index 0000000..4e25c6d
--- /dev/null
+++ b/libedataserver/e-source-registry.h
@@ -0,0 +1,207 @@
+/*
+ * e-source-registry.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_SOURCE_REGISTRY_H
+#define E_SOURCE_REGISTRY_H
+
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-authenticator.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_REGISTRY \
+	(e_source_registry_get_type ())
+#define E_SOURCE_REGISTRY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_REGISTRY, ESourceRegistry))
+#define E_SOURCE_REGISTRY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_REGISTRY, ESourceRegistryClass))
+#define E_IS_SOURCE_REGISTRY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_REGISTRY))
+#define E_IS_SOURCE_REGISTRY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_REGISTRY))
+#define E_SOURCE_REGISTRY_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_REGISTRY, ESourceRegistryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceRegistry ESourceRegistry;
+typedef struct _ESourceRegistryClass ESourceRegistryClass;
+typedef struct _ESourceRegistryPrivate ESourceRegistryPrivate;
+
+/**
+ * ESourceRegistry:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceRegistry {
+	GObject parent;
+	ESourceRegistryPrivate *priv;
+};
+
+struct _ESourceRegistryClass {
+	GObjectClass parent_class;
+
+	/* Signals */
+	void		(*source_added)		(ESourceRegistry *registry,
+						 ESource *source);
+	void		(*source_changed)	(ESourceRegistry *registry,
+						 ESource *source);
+	void		(*source_removed)	(ESourceRegistry *registry,
+						 ESource *source);
+	void		(*source_enabled)	(ESourceRegistry *registry,
+						 ESource *source);
+	void		(*source_disabled)	(ESourceRegistry *registry,
+						 ESource *source);
+};
+
+GType		e_source_registry_get_type	(void) G_GNUC_CONST;
+ESourceRegistry *
+		e_source_registry_new_sync	(GCancellable *cancellable,
+						 GError **error);
+void		e_source_registry_new		(GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+ESourceRegistry *
+		e_source_registry_new_finish	(GAsyncResult *result,
+						 GError **error);
+gboolean	e_source_registry_authenticate_sync
+						(ESourceRegistry *registry,
+						 ESource *source,
+						 ESourceAuthenticator *auth,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_source_registry_authenticate	(ESourceRegistry *registry,
+						 ESource *source,
+						 ESourceAuthenticator *auth,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_source_registry_authenticate_finish
+						(ESourceRegistry *registry,
+						 GAsyncResult *result,
+						 GError **error);
+gboolean	e_source_registry_commit_source_sync
+						(ESourceRegistry *registry,
+						 ESource *input_source,
+						 ESource **output_source,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_source_registry_commit_source	(ESourceRegistry *registry,
+						 ESource *input_source,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_source_registry_commit_source_finish
+						(ESourceRegistry *registry,
+						 GAsyncResult *result,
+						 ESource **output_source,
+						 GError **error);
+gboolean	e_source_registry_create_source_sync
+						(ESourceRegistry *registry,
+						 ESource *input_source,
+						 ESource **output_source,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_source_registry_create_source	(ESourceRegistry *registry,
+						 ESource *input_source,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_source_registry_create_source_finish
+						(ESourceRegistry *registry,
+						 GAsyncResult *result,
+						 ESource **output_source,
+						 GError **error);
+ESource *	e_source_registry_ref_source	(ESourceRegistry *registry,
+						 const gchar *uid);
+GList *		e_source_registry_list_sources	(ESourceRegistry *registry,
+						 const gchar *extension_name);
+ESource *	e_source_registry_find_extension
+						(ESourceRegistry *registry,
+						 ESource *source,
+						 const gchar *extension_name);
+GNode *		e_source_registry_build_display_tree
+						(ESourceRegistry *registry,
+						 const gchar *extension_name);
+void		e_source_registry_free_display_tree
+						(GNode *display_tree);
+void		e_source_registry_debug_dump	(ESourceRegistry *registry,
+						 const gchar *extension_name);
+
+/* The following is a front-end for the "org.gnome.Evolution.DefaultSources"
+ * GSettings schema, except that it gets and sets ESource objects instead of
+ * ESource UID strings. */
+
+ESource *	e_source_registry_ref_builtin_address_book
+						(ESourceRegistry *registry);
+ESource *	e_source_registry_ref_default_address_book
+						(ESourceRegistry *registry);
+void		e_source_registry_set_default_address_book
+						(ESourceRegistry *registry,
+						 ESource *default_source);
+ESource *	e_source_registry_ref_builtin_calendar
+						(ESourceRegistry *registry);
+ESource *	e_source_registry_ref_default_calendar
+						(ESourceRegistry *registry);
+void		e_source_registry_set_default_calendar
+						(ESourceRegistry *registry,
+						 ESource *default_source);
+ESource *	e_source_registry_ref_builtin_mail_account
+						(ESourceRegistry *registry);
+ESource *	e_source_registry_ref_default_mail_account
+						(ESourceRegistry *registry);
+void		e_source_registry_set_default_mail_account
+						(ESourceRegistry *registry,
+						 ESource *default_source);
+ESource *	e_source_registry_ref_default_mail_identity
+						(ESourceRegistry *registry);
+void		e_source_registry_set_default_mail_identity
+						(ESourceRegistry *registry,
+						 ESource *default_source);
+ESource *	e_source_registry_ref_builtin_memo_list
+						(ESourceRegistry *registry);
+ESource *	e_source_registry_ref_default_memo_list
+						(ESourceRegistry *registry);
+void		e_source_registry_set_default_memo_list
+						(ESourceRegistry *registry,
+						 ESource *default_source);
+ESource *	e_source_registry_ref_builtin_task_list
+						(ESourceRegistry *registry);
+ESource *	e_source_registry_ref_default_task_list
+						(ESourceRegistry *registry);
+void		e_source_registry_set_default_task_list
+						(ESourceRegistry *registry,
+						 ESource *default_source);
+ESource *	e_source_registry_ref_default_for_extension_name
+						(ESourceRegistry *registry,
+						 const gchar *extension_name);
+void		e_source_registry_set_default_for_extension_name
+						(ESourceRegistry *registry,
+						 const gchar *extension_name,
+						 ESource *default_source);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_REGISTRY_H */
diff --git a/libedataserver/e-source-security.c b/libedataserver/e-source-security.c
new file mode 100644
index 0000000..0f6c87b
--- /dev/null
+++ b/libedataserver/e-source-security.c
@@ -0,0 +1,327 @@
+/*
+ * e-source-security.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-security
+ * @include: libedataserver/e-source-security.h
+ * @short_description: #ESource extension for security settings
+ *
+ * The #ESourceSecurity extension tracks settings for establishing a
+ * secure connection with a remote server.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-security.h>
+ *
+ *   ESourceSecurity *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_SECURITY);
+ * ]|
+ **/
+
+#include "e-source-security.h"
+
+#define E_SOURCE_SECURITY_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_SECURITY, ESourceSecurityPrivate))
+
+#define SECURE_METHOD "tls"
+
+struct _ESourceSecurityPrivate {
+	GMutex *property_lock;
+	gchar *method;
+};
+
+enum {
+	PROP_0,
+	PROP_METHOD,
+	PROP_SECURE
+};
+
+G_DEFINE_TYPE (
+	ESourceSecurity,
+	e_source_security,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_security_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_METHOD:
+			e_source_security_set_method (
+				E_SOURCE_SECURITY (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SECURE:
+			e_source_security_set_secure (
+				E_SOURCE_SECURITY (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_security_get_property (GObject *object,
+                              guint property_id,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_METHOD:
+			g_value_take_string (
+				value,
+				e_source_security_dup_method (
+				E_SOURCE_SECURITY (object)));
+			return;
+
+		case PROP_SECURE:
+			g_value_set_boolean (
+				value,
+				e_source_security_get_secure (
+				E_SOURCE_SECURITY (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_security_finalize (GObject *object)
+{
+	ESourceSecurityPrivate *priv;
+
+	priv = E_SOURCE_SECURITY_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->method);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_security_parent_class)->finalize (object);
+}
+
+static void
+e_source_security_class_init (ESourceSecurityClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceSecurityPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_security_set_property;
+	object_class->get_property = source_security_get_property;
+	object_class->finalize = source_security_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_SECURITY;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_METHOD,
+		g_param_spec_string (
+			"method",
+			"Method",
+			"Security method",
+			"none",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SECURE,
+		g_param_spec_boolean (
+			"secure",
+			"Secure",
+			"Secure the network connection",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_source_security_init (ESourceSecurity *extension)
+{
+	extension->priv = E_SOURCE_SECURITY_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_security_get_method:
+ * @extension: an #ESourceSecurity
+ *
+ * Returns the method used to establish a secure network connection to a
+ * remote account.  There are no pre-defined method names; backends are
+ * free to set this however they wish.  If a secure connection is not
+ * desired, the convention is to set #ESourceSecurity:method to "none".
+ *
+ * Returns: the method used to establish a secure network connection
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_security_get_method (ESourceSecurity *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SECURITY (extension), NULL);
+
+	return extension->priv->method;
+}
+
+/**
+ * e_source_security_dup_method:
+ * @extension: an #ESourceSecurity
+ *
+ * Thread-safe variation of e_source_security_get_method().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceSecurity:method
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_security_dup_method (ESourceSecurity *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_SECURITY (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_security_get_method (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_security_set_method:
+ * @extension: an #ESourceSecurity
+ * @method: security method, or %NULL
+ *
+ * Sets the method used to establish a secure network connection to a
+ * remote account.  There are no pre-defined method names; backends are
+ * free to set this however they wish.  If a secure connection is not
+ * desired, the convention is to set #ESourceSecurity:method to "none".
+ * In keeping with that convention, #ESourceSecurity:method will be set
+ * to "none" if @method is %NULL or an empty string.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_security_set_method (ESourceSecurity *extension,
+                              const gchar *method)
+{
+	GObject *object;
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_SECURITY (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (method);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	/* Convert NULL to "none". */
+	if (duplicate == NULL)
+		duplicate = g_strdup ("none");
+
+	g_free (extension->priv->method);
+	extension->priv->method = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	object = G_OBJECT (extension);
+	g_object_freeze_notify (object);
+	g_object_notify (object, "method");
+	g_object_notify (object, "secure");
+	g_object_thaw_notify (object);
+}
+
+/**
+ * e_source_security_get_secure:
+ * @extension: an #ESourceSecurity
+ *
+ * This is a convenience function which returns whether a secure network
+ * connection is desired, regardless of the method used.  This relies on
+ * the convention of setting #ESourceSecurity:method to "none" when a
+ * secure network connection is <emphasis>not</emphasis> desired.
+ *
+ * Returns: whether a secure network connection is desired
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_security_get_secure (ESourceSecurity *extension)
+{
+	const gchar *method;
+
+	g_return_val_if_fail (E_IS_SOURCE_SECURITY (extension), FALSE);
+
+	method = e_source_security_get_method (extension);
+	g_return_val_if_fail (method != NULL, FALSE);
+
+	return (g_strcmp0 (method, "none") != 0);
+}
+
+/**
+ * e_source_security_set_secure:
+ * @extension: an #ESourceSecurity
+ * @secure: whether a secure network connection is desired
+ *
+ * This function provides a simpler way to set #ESourceSecurity:method
+ * when using a secure network connection is a yes or no option and the
+ * exact method name is unimportant.  If @secure is %FALSE, the
+ * #ESourceSecurity:method property is set to "none".  If @secure is
+ * %TRUE, the function assumes the backend will use Transport Layer
+ * Security and sets the #ESourceSecurity:method property to "tls".
+ *
+ * Since: 3.6
+ **/
+void
+e_source_security_set_secure (ESourceSecurity *extension,
+                              gboolean secure)
+{
+	const gchar *method;
+
+	g_return_if_fail (E_IS_SOURCE_SECURITY (extension));
+
+	method = secure ? SECURE_METHOD : "none";
+	e_source_security_set_method (extension, method);
+}
diff --git a/libedataserver/e-source-security.h b/libedataserver/e-source-security.h
new file mode 100644
index 0000000..28e2e85
--- /dev/null
+++ b/libedataserver/e-source-security.h
@@ -0,0 +1,87 @@
+/*
+ * e-source-security.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_SOURCE_SECURITY_H
+#define E_SOURCE_SECURITY_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_SECURITY \
+	(e_source_security_get_type ())
+#define E_SOURCE_SECURITY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_SECURITY, ESourceSecurity))
+#define E_SOURCE_SECURITY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_SECURITY, ESourceSecurityClass))
+#define E_IS_SOURCE_SECURITY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_SECURITY))
+#define E_IS_SOURCE_SECURITY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_SECURITY))
+#define E_SOURCE_SECURITY_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_SECURITY, ESourceSecurityClass))
+
+/**
+ * E_SOURCE_EXTENSION_SECURITY:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceSecurity.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_SECURITY "Security"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceSecurity ESourceSecurity;
+typedef struct _ESourceSecurityClass ESourceSecurityClass;
+typedef struct _ESourceSecurityPrivate ESourceSecurityPrivate;
+
+/**
+ * ESourceSecurity:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceSecurity {
+	ESourceExtension parent;
+	ESourceSecurityPrivate *priv;
+};
+
+struct _ESourceSecurityClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_security_get_type	(void) G_GNUC_CONST;
+const gchar *	e_source_security_get_method	(ESourceSecurity *extension);
+gchar *		e_source_security_dup_method	(ESourceSecurity *extension);
+void		e_source_security_set_method	(ESourceSecurity *extension,
+						 const gchar *method);
+gboolean	e_source_security_get_secure	(ESourceSecurity *extension);
+void		e_source_security_set_secure	(ESourceSecurity *extension,
+						 gboolean secure);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_SECURITY_H */
diff --git a/libedataserver/e-source-selectable.c b/libedataserver/e-source-selectable.c
new file mode 100644
index 0000000..c4a1271
--- /dev/null
+++ b/libedataserver/e-source-selectable.c
@@ -0,0 +1,301 @@
+/*
+ * e-source-selectable.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-selectable
+ * @include: libedataserver/e-source-selectable.h
+ * @short_description: Base class for selectable data sources
+ * @see_also: #ESourceAddressBook, #ESourceCalendar, #ESourceMemoList,
+ *            #ESourceTaskList
+ *
+ * #ESourceSelectable is an abstract base class for data sources
+ * that can be selected in an #ESourceSelector or similar widget.
+ **/
+
+#include "e-source-selectable.h"
+
+#define E_SOURCE_SELECTABLE_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_SELECTABLE, ESourceSelectablePrivate))
+
+struct _ESourceSelectablePrivate {
+	GMutex *property_lock;
+	gchar *color;
+	gboolean selected;
+};
+
+enum {
+	PROP_0,
+	PROP_COLOR,
+	PROP_SELECTED
+};
+
+G_DEFINE_ABSTRACT_TYPE (
+	ESourceSelectable,
+	e_source_selectable,
+	E_TYPE_SOURCE_BACKEND)
+
+static void
+source_selectable_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_COLOR:
+			e_source_selectable_set_color (
+				E_SOURCE_SELECTABLE (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SELECTED:
+			e_source_selectable_set_selected (
+				E_SOURCE_SELECTABLE (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_selectable_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_COLOR:
+			g_value_take_string (
+				value,
+				e_source_selectable_dup_color (
+				E_SOURCE_SELECTABLE (object)));
+			return;
+
+		case PROP_SELECTED:
+			g_value_set_boolean (
+				value,
+				e_source_selectable_get_selected (
+				E_SOURCE_SELECTABLE (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_selectable_finalize (GObject *object)
+{
+	ESourceSelectablePrivate *priv;
+
+	priv = E_SOURCE_SELECTABLE_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->color);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_selectable_parent_class)->finalize (object);
+}
+
+static void
+e_source_selectable_class_init (ESourceSelectableClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESourceSelectablePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_selectable_set_property;
+	object_class->get_property = source_selectable_get_property;
+	object_class->finalize = source_selectable_finalize;
+
+	/* We do not provide an extension name,
+	 * which is why the class is abstract. */
+
+	g_object_class_install_property (
+		object_class,
+		PROP_COLOR,
+		g_param_spec_string (
+			"color",
+			"Color",
+			"Textual specification of a color",
+			"#becedd",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SELECTED,
+		g_param_spec_boolean (
+			"selected",
+			"Selected",
+			"Whether the data source is selected",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_selectable_init (ESourceSelectable *extension)
+{
+	extension->priv = E_SOURCE_SELECTABLE_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_selectable_get_color:
+ * @extension: an #ESourceSelectable
+ *
+ * Returns the color specification for the #ESource to which @extension
+ * belongs.  A colored block is often displayed next to the data source's
+ * display name in user interfaces.
+ *
+ * Returns: the color specification for the #ESource
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_selectable_get_color (ESourceSelectable *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SELECTABLE (extension), NULL);
+
+	return extension->priv->color;
+}
+
+/**
+ * e_source_selectable_dup_color:
+ * @extension: an #ESourceSelectable
+ *
+ * Thread-safe variation of e_source_selectable_get_color().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceSelectable:color
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_selectable_dup_color (ESourceSelectable *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_SELECTABLE (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_selectable_get_color (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_selectable_set_color:
+ * @extension: an #ESourceSelectable
+ * @color: a color specification, or %NULL
+ *
+ * Sets the color specification for the #ESource to which @extension
+ * belongs.  A colored block is often displayed next to the data source's
+ * display name in user interfaces.
+ *
+ * The internal copy of @color is automatically stripped of leading and
+ * trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_selectable_set_color (ESourceSelectable *extension,
+                               const gchar *color)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_SELECTABLE (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (color);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->color);
+	extension->priv->color = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "color");
+}
+
+/**
+ * e_source_selectable_get_selected:
+ * @extension: an #ESourceSelectable
+ *
+ * Returns the selected state of the #ESource to which @extension belongs.
+ * The selected state is often represented as a checkbox next to the data
+ * source's display name in user interfaces.
+ *
+ * Returns: the selected state for the #ESource
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_selectable_get_selected (ESourceSelectable *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SELECTABLE (extension), FALSE);
+
+	return extension->priv->selected;
+}
+
+/**
+ * e_source_selectable_set_selected:
+ * @extension: an #ESourceSelectable
+ * @selected: selected state
+ *
+ * Sets the selected state for the #ESource to which @extension belongs.
+ * The selected state is often represented as a checkbox next to the data
+ * source's display name in user interfaces.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_selectable_set_selected (ESourceSelectable *extension,
+                                  gboolean selected)
+{
+	g_return_if_fail (E_IS_SOURCE_SELECTABLE (extension));
+
+	extension->priv->selected = selected;
+
+	g_object_notify (G_OBJECT (extension), "selected");
+}
+
diff --git a/libedataserver/e-source-selectable.h b/libedataserver/e-source-selectable.h
new file mode 100644
index 0000000..07d43b0
--- /dev/null
+++ b/libedataserver/e-source-selectable.h
@@ -0,0 +1,79 @@
+/*
+ * e-source-selectable.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_SOURCE_SELECTABLE_H
+#define E_SOURCE_SELECTABLE_H
+
+#include <libedataserver/e-source-backend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_SELECTABLE \
+	(e_source_selectable_get_type ())
+#define E_SOURCE_SELECTABLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_SELECTABLE, ESourceSelectable))
+#define E_SOURCE_SELECTABLE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_SELECTABLE, ESourceSelectableClass))
+#define E_IS_SOURCE_SELECTABLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_SELECTABLE))
+#define E_IS_SOURCE_SELECTABLE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_SELECTABLE))
+#define E_SOURCE_SELECTABLE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_SELECTABLE, ESourceSelectableClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceSelectable ESourceSelectable;
+typedef struct _ESourceSelectableClass ESourceSelectableClass;
+typedef struct _ESourceSelectablePrivate ESourceSelectablePrivate;
+
+/**
+ * ESourceSelectable:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceSelectable {
+	ESourceBackend parent;
+	ESourceSelectablePrivate *priv;
+};
+
+struct _ESourceSelectableClass {
+	ESourceBackendClass parent_class;
+};
+
+GType		e_source_selectable_get_type	(void) G_GNUC_CONST;
+const gchar *	e_source_selectable_get_color	(ESourceSelectable *extension);
+gchar *		e_source_selectable_dup_color	(ESourceSelectable *extension);
+void		e_source_selectable_set_color	(ESourceSelectable *extension,
+						 const gchar *color);
+gboolean	e_source_selectable_get_selected
+						(ESourceSelectable *extension);
+void		e_source_selectable_set_selected
+						(ESourceSelectable *extension,
+						 gboolean selected);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_SELECTABLE_H */
diff --git a/libedataserver/e-source-smime.c b/libedataserver/e-source-smime.c
new file mode 100644
index 0000000..2099bb2
--- /dev/null
+++ b/libedataserver/e-source-smime.c
@@ -0,0 +1,681 @@
+/*
+ * e-source-smime.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-smime
+ * @include: libedataserver/e-source-smime.h
+ * @short_description: #ESource extension for S/MIME settings
+ *
+ * The #ESourceSMIME extension tracks Secure/Multipurpose Internet Mail
+ * Extensions (S/MIME) settings to be applied to outgoing mail messages.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-smime.h>
+ *
+ *   ESourceSMIME *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_SMIME);
+ * ]|
+ **/
+
+#include "e-source-smime.h"
+
+#define E_SOURCE_SMIME_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_SMIME, ESourceSMIMEPrivate))
+
+struct _ESourceSMIMEPrivate {
+	GMutex *property_lock;
+	gchar *encryption_certificate;
+	gchar *signing_algorithm;
+	gchar *signing_certificate;
+
+	gboolean encrypt_by_default;
+	gboolean encrypt_to_self;
+	gboolean sign_by_default;
+};
+
+enum {
+	PROP_0,
+	PROP_ENCRYPTION_CERTIFICATE,
+	PROP_ENCRYPT_BY_DEFAULT,
+	PROP_ENCRYPT_TO_SELF,
+	PROP_SIGNING_ALGORITHM,
+	PROP_SIGNING_CERTIFICATE,
+	PROP_SIGN_BY_DEFAULT
+};
+
+G_DEFINE_TYPE (
+	ESourceSMIME,
+	e_source_smime,
+	E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_smime_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ENCRYPTION_CERTIFICATE:
+			e_source_smime_set_encryption_certificate (
+				E_SOURCE_SMIME (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_ENCRYPT_BY_DEFAULT:
+			e_source_smime_set_encrypt_by_default (
+				E_SOURCE_SMIME (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_ENCRYPT_TO_SELF:
+			e_source_smime_set_encrypt_to_self (
+				E_SOURCE_SMIME (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_SIGNING_ALGORITHM:
+			e_source_smime_set_signing_algorithm (
+				E_SOURCE_SMIME (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SIGNING_CERTIFICATE:
+			e_source_smime_set_signing_certificate (
+				E_SOURCE_SMIME (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SIGN_BY_DEFAULT:
+			e_source_smime_set_sign_by_default (
+				E_SOURCE_SMIME (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_smime_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ENCRYPTION_CERTIFICATE:
+			g_value_take_string (
+				value,
+				e_source_smime_dup_encryption_certificate (
+				E_SOURCE_SMIME (object)));
+			return;
+
+		case PROP_ENCRYPT_BY_DEFAULT:
+			g_value_set_boolean (
+				value,
+				e_source_smime_get_encrypt_by_default (
+				E_SOURCE_SMIME (object)));
+			return;
+
+		case PROP_ENCRYPT_TO_SELF:
+			g_value_set_boolean (
+				value,
+				e_source_smime_get_encrypt_to_self (
+				E_SOURCE_SMIME (object)));
+			return;
+
+		case PROP_SIGNING_ALGORITHM:
+			g_value_take_string (
+				value,
+				e_source_smime_dup_signing_algorithm (
+				E_SOURCE_SMIME (object)));
+			return;
+
+		case PROP_SIGNING_CERTIFICATE:
+			g_value_take_string (
+				value,
+				e_source_smime_dup_signing_certificate (
+				E_SOURCE_SMIME (object)));
+			return;
+
+		case PROP_SIGN_BY_DEFAULT:
+			g_value_set_boolean (
+				value,
+				e_source_smime_get_sign_by_default (
+				E_SOURCE_SMIME (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_smime_finalize (GObject *object)
+{
+	ESourceSMIMEPrivate *priv;
+
+	priv = E_SOURCE_SMIME_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->encryption_certificate);
+	g_free (priv->signing_algorithm);
+	g_free (priv->signing_certificate);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_smime_parent_class)->finalize (object);
+}
+
+static void
+e_source_smime_class_init (ESourceSMIMEClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceSMIMEPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_smime_set_property;
+	object_class->get_property = source_smime_get_property;
+	object_class->finalize = source_smime_finalize;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_SMIME;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ENCRYPTION_CERTIFICATE,
+		g_param_spec_string (
+			"encryption-certificate",
+			"Encryption Certificate",
+			"S/MIME certificate for encrypting messages",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ENCRYPT_BY_DEFAULT,
+		g_param_spec_boolean (
+			"encrypt-by-default",
+			"Encrypt By Default",
+			"Encrypt outgoing messages by default",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ENCRYPT_TO_SELF,
+		g_param_spec_boolean (
+			"encrypt-to-self",
+			"Encrypt To Self",
+			"Always encrypt to myself",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGNING_ALGORITHM,
+		g_param_spec_string (
+			"signing-algorithm",
+			"Signing Algorithm",
+			"Hash algorithm used to sign messages",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGNING_CERTIFICATE,
+		g_param_spec_string (
+			"signing-certificate",
+			"Signing Certificate",
+			"S/MIME certificate for signing messages",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGN_BY_DEFAULT,
+		g_param_spec_boolean (
+			"sign-by-default",
+			"Sign By Default",
+			"Sign outgoing messages by default",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_smime_init (ESourceSMIME *extension)
+{
+	extension->priv = E_SOURCE_SMIME_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+/**
+ * e_source_smime_get_encryption_certificate:
+ * @extension: an #ESourceSMIME
+ *
+ * Returns the S/MIME certificate name used to encrypt messages.
+ *
+ * Returns: the certificate name used to encrypt messages
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_smime_get_encryption_certificate (ESourceSMIME *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL);
+
+	return extension->priv->encryption_certificate;
+}
+
+/**
+ * e_source_smime_dup_encryption_certificate:
+ * @extension: an #ESourceSMIME
+ *
+ * Thread-safe variation of e_source_smime_get_encryption_certificate().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceSMIME:encryption-certificate
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_smime_dup_encryption_certificate (ESourceSMIME *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_smime_get_encryption_certificate (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_smime_set_encryption_certificate:
+ * @extension: an #ESourceSMIME
+ * @encryption_certificate: the certificate name used to encrypt messages
+ *
+ * Sets the certificate name used to encrypt messages.
+ *
+ * The internal copy of @encryption_certificate is automatically stripped
+ * of leading and trailing whitespace.  If the resulting string is empty,
+ * %NULL is set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_smime_set_encryption_certificate (ESourceSMIME *extension,
+                                           const gchar *encryption_certificate)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_SMIME (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (encryption_certificate);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->encryption_certificate);
+	extension->priv->encryption_certificate = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "encryption-certificate");
+}
+
+/**
+ * e_source_smime_get_encrypt_by_default:
+ * @extension: an #ESourceSMIME
+ *
+ * Returns whether to encrypt outgoing messages by default using S/MIME
+ * software such as Mozilla Network Security Services (NSS).
+ *
+ * Returns: whether to encrypt outgoing messages by default
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_smime_get_encrypt_by_default (ESourceSMIME *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), FALSE);
+
+	return extension->priv->encrypt_by_default;
+}
+
+/**
+ * e_source_smime_set_encrypt_by_default:
+ * @extension: an #ESourceSMIME
+ * @encrypt_by_default: whether to encrypt outgoing messages by default
+ *
+ * Sets whether to encrypt outgoing messages by default using S/MIME
+ * software such as Mozilla Network Security Services (NSS).
+ *
+ * Since: 3.6
+ **/
+void
+e_source_smime_set_encrypt_by_default (ESourceSMIME *extension,
+                                       gboolean encrypt_by_default)
+{
+	g_return_if_fail (E_IS_SOURCE_SMIME (extension));
+
+	extension->priv->encrypt_by_default = encrypt_by_default;
+
+	g_object_notify (G_OBJECT (extension), "encrypt-by-default");
+}
+
+/**
+ * e_source_smime_get_encrypt_to_self:
+ * @extension: an #ESourceSMIME
+ *
+ * Returns whether to "encrypt-to-self" when sending encrypted messages.
+ *
+ * Returns: whether to "encrypt-to-self"
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_smime_get_encrypt_to_self (ESourceSMIME *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), FALSE);
+
+	return extension->priv->encrypt_to_self;
+}
+
+/**
+ * e_source_smime_set_encrypt_to_self:
+ * @extension: an #ESourceSMIME
+ * @encrypt_to_self: whether to "encrypt-to-self"
+ *
+ * Sets whether to "encrypt-to-self" when sending encrypted messages.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_smime_set_encrypt_to_self (ESourceSMIME *extension,
+                                    gboolean encrypt_to_self)
+{
+	g_return_if_fail (E_IS_SOURCE_SMIME (extension));
+
+	extension->priv->encrypt_to_self = encrypt_to_self;
+
+	g_object_notify (G_OBJECT (extension), "encrypt-to-self");
+}
+
+/**
+ * e_source_smime_get_signing_algorithm:
+ * @extension: an #ESourceSMIME
+ *
+ * Returns the name of the hash algorithm used to digitally sign outgoing
+ * messages.
+ *
+ * Returns: the signing algorithm for outgoing messages
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_smime_get_signing_algorithm (ESourceSMIME *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL);
+
+	return extension->priv->signing_algorithm;
+}
+
+/**
+ * e_source_smime_dup_signing_algorithm:
+ * @extension: an #ESourceSMIME
+ *
+ * Thread-safe variation of e_source_smime_get_signing_algorithm().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceSMIME:signing-algorithm
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_smime_dup_signing_algorithm (ESourceSMIME *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_smime_get_signing_algorithm (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_smime_set_signing_algorithm:
+ * @extension: an #ESourceSMIME
+ * @signing_algorithm: the signing algorithm for outgoing messages
+ *
+ * Sets the name of the hash algorithm used to digitally sign outgoing
+ * messages.
+ *
+ * The internal copy of @signing_algorithm is automatically stripped of
+ * leading and trailing whitespace.  If the resulting string is empty,
+ * %NULL is set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_smime_set_signing_algorithm (ESourceSMIME *extension,
+                                      const gchar *signing_algorithm)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_SMIME (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (signing_algorithm);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->signing_algorithm);
+	extension->priv->signing_algorithm = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "signing-algorithm");
+}
+
+/**
+ * e_source_smime_get_signing_certificate:
+ * @extension: an #ESourceSMIME
+ *
+ * Returns the S/MIME certificate name used to sign messages.
+ *
+ * Returns: the certificate name used to sign messages
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_smime_get_signing_certificate (ESourceSMIME *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL);
+
+	return extension->priv->signing_certificate;
+}
+
+/**
+ * e_source_smime_dup_signing_certificate:
+ * @extension: an #ESourceSMIME
+ *
+ * Thread-safe variation of e_source_smime_get_signing_certificate().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceSMIME:signing-certificate
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_smime_dup_signing_certificate (ESourceSMIME *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_smime_get_signing_certificate (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_smime_set_signing_certificate:
+ * @extension: an #ESourceSMIME
+ * @signing_certificate: the certificate name used to sign messages
+ *
+ * Sets the S/MIME certificate name used to sign messages.
+ *
+ * The internal copy of @signing_certificate is automatically stripped
+ * of leading and trailing whitespace.  If the resulting string is empty,
+ * %NULL is set instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_smime_set_signing_certificate (ESourceSMIME *extension,
+                                        const gchar *signing_certificate)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_SMIME (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (signing_certificate);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->signing_certificate);
+	extension->priv->signing_certificate = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "signing-certificate");
+}
+
+/**
+ * e_source_smime_get_sign_by_default:
+ * @extension: an #ESourceSMIME
+ *
+ * Returns whether to digitally sign outgoing messages by default using
+ * S/MIME software such as Mozilla Network Security Services (NSS).
+ *
+ * Returns: whether to sign outgoing messages by default
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_smime_get_sign_by_default (ESourceSMIME *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), FALSE);
+
+	return extension->priv->sign_by_default;
+}
+
+/**
+ * e_source_smime_set_sign_by_default:
+ * @extension: an #ESourceSMIME
+ * @sign_by_default: whether to sign outgoing messages by default
+ *
+ * Sets whether to digitally sign outgoing messages by default using
+ * S/MIME software such as Mozilla Network Security Services (NSS).
+ *
+ * Since: 3.6
+ **/
+void
+e_source_smime_set_sign_by_default (ESourceSMIME *extension,
+                                    gboolean sign_by_default)
+{
+	g_return_if_fail (E_IS_SOURCE_SMIME (extension));
+
+	extension->priv->sign_by_default = sign_by_default;
+
+	g_object_notify (G_OBJECT (extension), "sign-by-default");
+}
+
diff --git a/libedataserver/e-source-smime.h b/libedataserver/e-source-smime.h
new file mode 100644
index 0000000..841727f
--- /dev/null
+++ b/libedataserver/e-source-smime.h
@@ -0,0 +1,117 @@
+/*
+ * e-source-smime.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_SOURCE_SMIME_H
+#define E_SOURCE_SMIME_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_SMIME \
+	(e_source_smime_get_type ())
+#define E_SOURCE_SMIME(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_SMIME, ESourceSMIME))
+#define E_SOURCE_SMIME_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_SMIME, ESourceSMIMEClass))
+#define E_IS_SOURCE_SMIME(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_SMIME))
+#define E_IS_SOURCE_SMIME_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_SMIME))
+#define E_SOURCE_SMIME_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_SMIME, ESourceSMIMEClass))
+
+/**
+ * E_SOURCE_EXTENSION_SMIME:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceSMIME.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_SMIME "Secure MIME (S/MIME)"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceSMIME ESourceSMIME;
+typedef struct _ESourceSMIMEClass ESourceSMIMEClass;
+typedef struct _ESourceSMIMEPrivate ESourceSMIMEPrivate;
+
+/**
+ * ESourceSMIME:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceSMIME {
+	ESourceExtension parent;
+	ESourceSMIMEPrivate *priv;
+};
+
+struct _ESourceSMIMEClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_smime_get_type		(void) G_GNUC_CONST;
+const gchar *	e_source_smime_get_encryption_certificate
+						(ESourceSMIME *extension);
+gchar *		e_source_smime_dup_encryption_certificate
+						(ESourceSMIME *extension);
+void		e_source_smime_set_encryption_certificate
+						(ESourceSMIME *extension,
+						 const gchar *encryption_certificate);
+gboolean	e_source_smime_get_encrypt_by_default
+						(ESourceSMIME *extension);
+void		e_source_smime_set_encrypt_by_default
+						(ESourceSMIME *extension,
+						 gboolean encrypt_by_default);
+gboolean	e_source_smime_get_encrypt_to_self
+						(ESourceSMIME *extension);
+void		e_source_smime_set_encrypt_to_self
+						(ESourceSMIME *extension,
+						 gboolean encrypt_to_self);
+const gchar *	e_source_smime_get_signing_algorithm
+						(ESourceSMIME *extension);
+gchar *		e_source_smime_dup_signing_algorithm
+						(ESourceSMIME *extension);
+void		e_source_smime_set_signing_algorithm
+						(ESourceSMIME *extension,
+						 const gchar *signing_algorithm);
+const gchar *	e_source_smime_get_signing_certificate
+						(ESourceSMIME *extension);
+gchar *		e_source_smime_dup_signing_certificate
+						(ESourceSMIME *extension);
+void		e_source_smime_set_signing_certificate
+						(ESourceSMIME *extension,
+						 const gchar *signing_certificate);
+gboolean	e_source_smime_get_sign_by_default
+						(ESourceSMIME *extension);
+void		e_source_smime_set_sign_by_default
+						(ESourceSMIME *extension,
+						 gboolean sign_by_default);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_SMIME_H */
+
diff --git a/libedataserver/e-source-webdav.c b/libedataserver/e-source-webdav.c
new file mode 100644
index 0000000..eeaf755
--- /dev/null
+++ b/libedataserver/e-source-webdav.c
@@ -0,0 +1,1083 @@
+/*
+ * e-source-webdav.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-source-webdav
+ * @include: libedataserver/e-source-webdav.h
+ * @short_description: #ESource extension for WebDAV settings
+ *
+ * The #ESourceWebdav extension tracks settings for accessing resources
+ * on a remote WebDAV server.
+ *
+ * This class exists in libedataserver because we have several
+ * WebDAV-based backends.  Each of these backends is free to use
+ * this class directly or subclass it with additional settings.
+ * Subclasses should override the extension name.
+ *
+ * The #SoupURI is parsed into components and distributed across
+ * several other built-in extensions such as #ESourceAuthentication
+ * and #ESourceSecurity.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ *   #include <libedataserver/e-source-webdav.h>
+ *
+ *   ESourceWebdav *extension;
+ *
+ *   extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ * ]|
+ **/
+
+#include "e-source-webdav.h"
+
+#include <libedataserver/e-source-authentication.h>
+#include <libedataserver/e-source-security.h>
+
+#define E_SOURCE_WEBDAV_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdavPrivate))
+
+struct _ESourceWebdavPrivate {
+	GMutex *property_lock;
+	gchar *display_name;
+	gchar *email_address;
+	gchar *resource_path;
+	gboolean avoid_ifmatch;
+	gboolean calendar_auto_schedule;
+	gboolean ignore_invalid_cert;
+	SoupURI *uri;
+};
+
+enum {
+	PROP_0,
+	PROP_AVOID_IFMATCH,
+	PROP_CALENDAR_AUTO_SCHEDULE,
+	PROP_DISPLAY_NAME,
+	PROP_EMAIL_ADDRESS,
+	PROP_IGNORE_INVALID_CERT,
+	PROP_RESOURCE_PATH,
+	PROP_SOUP_URI
+};
+
+G_DEFINE_TYPE (
+	ESourceWebdav,
+	e_source_webdav,
+	E_TYPE_SOURCE_EXTENSION)
+
+static gboolean
+source_webdav_host_to_soup_uri (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	GObject *target;
+	SoupURI *soup_uri;
+	const gchar *host;
+
+	host = g_value_get_string (source_value);
+
+	target = g_binding_get_target (binding);
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE);
+
+	soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target));
+	soup_uri_set_host (soup_uri, host);
+	g_value_take_boxed (target_value, soup_uri);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_soup_uri_to_host (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	SoupURI *soup_uri;
+
+	soup_uri = g_value_get_boxed (source_value);
+	g_value_set_string (target_value, soup_uri->host);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_path_to_soup_uri (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	GObject *target;
+	SoupURI *soup_uri;
+	const gchar *path;
+
+	path = g_value_get_string (source_value);
+
+	/* soup_uri_set_path() warns on NULL. */
+	if (path == NULL)
+		path = "";
+
+	target = g_binding_get_target (binding);
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE);
+
+	soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target));
+	soup_uri_set_path (soup_uri, path);
+	g_value_take_boxed (target_value, soup_uri);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_soup_uri_to_path (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	SoupURI *soup_uri;
+
+	soup_uri = g_value_get_boxed (source_value);
+	g_value_set_string (target_value, soup_uri->path);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_port_to_soup_uri (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	GObject *target;
+	SoupURI *soup_uri;
+	guint port;
+
+	port = g_value_get_uint (source_value);
+
+	target = g_binding_get_target (binding);
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE);
+
+	soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target));
+	soup_uri_set_port (soup_uri, port);
+	g_value_take_boxed (target_value, soup_uri);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_soup_uri_to_port (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	SoupURI *soup_uri;
+
+	soup_uri = g_value_get_boxed (source_value);
+	g_value_set_uint (target_value, soup_uri->port);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_secure_to_soup_uri (GBinding *binding,
+                                  const GValue *source_value,
+                                  GValue *target_value,
+                                  gpointer user_data)
+{
+	GObject *target;
+	SoupURI *soup_uri;
+	gboolean secure;
+
+	secure = g_value_get_boolean (source_value);
+
+	target = g_binding_get_target (binding);
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE);
+
+	soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target));
+	if (secure)
+		soup_uri_set_scheme (soup_uri, SOUP_URI_SCHEME_HTTPS);
+	else
+		soup_uri_set_scheme (soup_uri, SOUP_URI_SCHEME_HTTP);
+	g_value_take_boxed (target_value, soup_uri);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_soup_uri_to_secure (GBinding *binding,
+                                  const GValue *source_value,
+                                  GValue *target_value,
+                                  gpointer user_data)
+{
+	SoupURI *soup_uri;
+	gboolean secure;
+
+	soup_uri = g_value_get_boxed (source_value);
+	secure = (soup_uri->scheme == SOUP_URI_SCHEME_HTTPS);
+	g_value_set_boolean (target_value, secure);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_user_to_soup_uri (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	GObject *target;
+	SoupURI *soup_uri;
+	const gchar *user;
+
+	user = g_value_get_string (source_value);
+
+	target = g_binding_get_target (binding);
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE);
+
+	soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target));
+	soup_uri_set_user (soup_uri, user);
+	g_value_take_boxed (target_value, soup_uri);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_soup_uri_to_user (GBinding *binding,
+                                const GValue *source_value,
+                                GValue *target_value,
+                                gpointer user_data)
+{
+	SoupURI *soup_uri;
+
+	soup_uri = g_value_get_boxed (source_value);
+	g_value_set_string (target_value, soup_uri->user);
+
+	return TRUE;
+}
+
+static gboolean
+source_webdav_user_to_method (GBinding *binding,
+                              const GValue *source_value,
+                              GValue *target_value,
+                              gpointer user_data)
+{
+	const gchar *user;
+
+	user = g_value_get_string (source_value);
+	if (user == NULL || *user == '\0')
+		g_value_set_string (target_value, "none");
+	else
+		g_value_set_string (target_value, "plain/password");
+
+	return TRUE;
+}
+
+static void
+source_webdav_set_property (GObject *object,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AVOID_IFMATCH:
+			e_source_webdav_set_avoid_ifmatch (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_CALENDAR_AUTO_SCHEDULE:
+			e_source_webdav_set_calendar_auto_schedule (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_DISPLAY_NAME:
+			e_source_webdav_set_display_name (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_EMAIL_ADDRESS:
+			e_source_webdav_set_email_address (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_IGNORE_INVALID_CERT:
+			e_source_webdav_set_ignore_invalid_cert (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_RESOURCE_PATH:
+			e_source_webdav_set_resource_path (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SOUP_URI:
+			e_source_webdav_set_soup_uri (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_boxed (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_webdav_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AVOID_IFMATCH:
+			g_value_set_boolean (
+				value,
+				e_source_webdav_get_avoid_ifmatch (
+				E_SOURCE_WEBDAV (object)));
+			return;
+
+		case PROP_CALENDAR_AUTO_SCHEDULE:
+			g_value_set_boolean (
+				value,
+				e_source_webdav_get_calendar_auto_schedule (
+				E_SOURCE_WEBDAV (object)));
+			return;
+
+		case PROP_DISPLAY_NAME:
+			g_value_take_string (
+				value,
+				e_source_webdav_dup_display_name (
+				E_SOURCE_WEBDAV (object)));
+			return;
+
+		case PROP_EMAIL_ADDRESS:
+			g_value_take_string (
+				value,
+				e_source_webdav_dup_email_address (
+				E_SOURCE_WEBDAV (object)));
+			return;
+
+		case PROP_IGNORE_INVALID_CERT:
+			g_value_set_boolean (
+				value,
+				e_source_webdav_get_ignore_invalid_cert (
+				E_SOURCE_WEBDAV (object)));
+			return;
+
+		case PROP_RESOURCE_PATH:
+			g_value_take_string (
+				value,
+				e_source_webdav_dup_resource_path (
+				E_SOURCE_WEBDAV (object)));
+			return;
+
+		case PROP_SOUP_URI:
+			g_value_take_boxed (
+				value,
+				e_source_webdav_dup_soup_uri (
+				E_SOURCE_WEBDAV (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_webdav_finalize (GObject *object)
+{
+	ESourceWebdavPrivate *priv;
+
+	priv = E_SOURCE_WEBDAV_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->display_name);
+	g_free (priv->email_address);
+	g_free (priv->resource_path);
+
+	soup_uri_free (priv->uri);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_webdav_parent_class)->finalize (object);
+}
+
+static void
+source_webdav_constructed (GObject *object)
+{
+	ESource *source;
+	ESourceExtension *this_extension;
+	ESourceExtension *other_extension;
+	const gchar *extension_name;
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_source_webdav_parent_class)->constructed (object);
+
+	this_extension = E_SOURCE_EXTENSION (object);
+	source = e_source_extension_get_source (this_extension);
+
+	g_object_bind_property_full (
+		this_extension, "resource-path",
+		this_extension, "soup-uri",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		source_webdav_path_to_soup_uri,
+		source_webdav_soup_uri_to_path,
+		NULL, (GDestroyNotify) NULL);
+
+	/* Bind to properties of other extensions for convenience. */
+
+	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+	other_extension = e_source_get_extension (source, extension_name);
+
+	g_object_bind_property_full (
+		other_extension, "host",
+		this_extension, "soup-uri",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		source_webdav_host_to_soup_uri,
+		source_webdav_soup_uri_to_host,
+		NULL, (GDestroyNotify) NULL);
+
+	g_object_bind_property_full (
+		other_extension, "port",
+		this_extension, "soup-uri",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		source_webdav_port_to_soup_uri,
+		source_webdav_soup_uri_to_port,
+		NULL, (GDestroyNotify) NULL);
+
+	g_object_bind_property_full (
+		other_extension, "user",
+		this_extension, "soup-uri",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		source_webdav_user_to_soup_uri,
+		source_webdav_soup_uri_to_user,
+		NULL, (GDestroyNotify) NULL);
+
+	g_object_bind_property_full (
+		other_extension, "user",
+		other_extension, "method",
+		G_BINDING_SYNC_CREATE,
+		source_webdav_user_to_method,
+		NULL,
+		NULL, (GDestroyNotify) NULL);
+
+	extension_name = E_SOURCE_EXTENSION_SECURITY;
+	other_extension = e_source_get_extension (source, extension_name);
+
+	g_object_bind_property_full (
+		other_extension, "secure",
+		this_extension, "soup-uri",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		source_webdav_secure_to_soup_uri,
+		source_webdav_soup_uri_to_secure,
+		NULL, (GDestroyNotify) NULL);
+}
+
+static void
+e_source_webdav_class_init (ESourceWebdavClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceWebdavPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_webdav_set_property;
+	object_class->get_property = source_webdav_get_property;
+	object_class->finalize = source_webdav_finalize;
+	object_class->constructed = source_webdav_constructed;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_AVOID_IFMATCH,
+		g_param_spec_boolean (
+			"avoid-ifmatch",
+			"Avoid If-Match",
+			"Work around a bug in old Apache servers",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CALENDAR_AUTO_SCHEDULE,
+		g_param_spec_boolean (
+			"calendar-auto-schedule",
+			"Calendar Auto-Schedule",
+			"Whether the server handles meeting "
+			"invitations (CalDAV-only)",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_DISPLAY_NAME,
+		g_param_spec_string (
+			"display-name",
+			"Display Name",
+			"Display name of the WebDAV resource",
+			"",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_EMAIL_ADDRESS,
+		g_param_spec_string (
+			"email-address",
+			"Email Address",
+			"The user's email address",
+			"",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_IGNORE_INVALID_CERT,
+		g_param_spec_boolean (
+			"ignore-invalid-cert",
+			"Ignore Invalid Cert",
+			"Ignore invalid SSL certificates",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_RESOURCE_PATH,
+		g_param_spec_string (
+			"resource-path",
+			"Resource Path",
+			"Absolute path to a WebDAV resource",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SOUP_URI,
+		g_param_spec_boxed (
+			"soup-uri",
+			"SoupURI",
+			"WebDAV service as a SoupURI",
+			SOUP_TYPE_URI,
+			G_PARAM_READWRITE));
+}
+
+static void
+e_source_webdav_init (ESourceWebdav *extension)
+{
+	extension->priv = E_SOURCE_WEBDAV_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+
+	/* Initialize this enough for SOUP_URI_IS_VALID() to pass. */
+	extension->priv->uri = soup_uri_new (NULL);
+	extension->priv->uri->scheme = SOUP_URI_SCHEME_HTTP;
+	extension->priv->uri->path = g_strdup ("");
+}
+
+/**
+ * e_source_webdav_get_avoid_ifmatch:
+ * @extension: an #ESourceWebdav
+ *
+ * This setting works around a
+ * <ulink url="https://issues.apache.org/bugzilla/show_bug.cgi?id=38034";>
+ * bug</ulink> in older Apache mod_dav versions.
+ *
+ * <note>
+ *   <para>
+ *     We may deprecate this once Apache 2.2.8 or newer becomes
+ *     sufficiently ubiquitous, or we figure out a way to detect
+ *     and work around the bug automatically.
+ *   </para>
+ * </note>
+ *
+ * Returns: whether the WebDAV server is known to exhibit the bug
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_webdav_get_avoid_ifmatch (ESourceWebdav *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE);
+
+	return extension->priv->avoid_ifmatch;
+}
+
+/**
+ * e_source_webdav_set_avoid_ifmatch:
+ * @extension: an #ESourceWebdav
+ * @avoid_ifmatch: whether the WebDAV server is known to exhibit the bug
+ *
+ * This setting works around a
+ * <ulink url="https://issues.apache.org/bugzilla/show_bug.cgi?id=38034";>
+ * bug</ulink> in older Apache mod_dav versions.
+ *
+ * <note>
+ *   <para>
+ *     We may deprecate this once Apache 2.2.8 or newer becomes
+ *     sufficiently ubiquitous, or we figure out a way to detect
+ *     and work around the bug automatically.
+ *   </para>
+ * </note>
+ *
+ * Since: 3.6
+ **/
+void
+e_source_webdav_set_avoid_ifmatch (ESourceWebdav *extension,
+                                   gboolean avoid_ifmatch)
+{
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	extension->priv->avoid_ifmatch = avoid_ifmatch;
+
+	g_object_notify (G_OBJECT (extension), "avoid-ifmatch");
+}
+
+/**
+ * e_source_webdav_get_calendar_auto_schedule:
+ * @extension: an #ESourceWebdav
+ *
+ * FIXME Document me!
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_webdav_get_calendar_auto_schedule (ESourceWebdav *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE);
+
+	return extension->priv->calendar_auto_schedule;
+}
+
+/**
+ * e_source_webdav_set_calendar_auto_schedule:
+ * @extension: an #ESourceWebdav
+ * @calendar_auto_schedule: whether the server supports the
+ * "calendar-auto-schedule" feature of CalDAV
+ *
+ * Since: 3.6
+ **/
+void
+e_source_webdav_set_calendar_auto_schedule (ESourceWebdav *extension,
+                                            gboolean calendar_auto_schedule)
+{
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	extension->priv->calendar_auto_schedule = calendar_auto_schedule;
+
+	g_object_notify (G_OBJECT (extension), "calendar-auto-schedule");
+}
+
+/**
+ * e_source_webdav_get_display_name:
+ * @extension: an #ESourceWebdav
+ *
+ * Returns the last known display name of a WebDAV resource, which may
+ * differ from the #ESource:display-name property of the #ESource to which
+ * @extension belongs.
+ *
+ * Returns: the display name of the WebDAV resource
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_webdav_get_display_name (ESourceWebdav *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	return extension->priv->display_name;
+}
+
+/**
+ * e_source_webdav_dup_display_name:
+ * @extension: an #ESourceWebdav
+ *
+ * Thread-safe variation of e_source_webdav_get_display_name().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceWebdav:display-name
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_webdav_dup_display_name (ESourceWebdav *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_webdav_get_display_name (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_webdav_set_display_name:
+ * @extension: an #ESourceWebdav
+ * @display_name: the display name of the WebDAV resource
+ *
+ * Updates the last known display name of a WebDAV resource, which may
+ * differ from the #ESource:display-name property of the #ESource to which
+ * @extension belongs.
+ *
+ * The internal copy of @display_name is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_webdav_set_display_name (ESourceWebdav *extension,
+                                  const gchar *display_name)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (display_name);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->display_name);
+	extension->priv->display_name = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "display-name");
+}
+
+/**
+ * e_source_webdav_get_email_address:
+ * @extension: an #ESourceWebdav
+ *
+ * Returns the user's email address which can be passed to a CalDAV server
+ * if the user wishes to receive scheduling messages.
+ *
+ * Returns: the user's email address
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_webdav_get_email_address (ESourceWebdav *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	return extension->priv->email_address;
+}
+
+/**
+ * e_source_webdav_dup_email_address:
+ * @extension: an #ESourceWebdav
+ *
+ * Thread-safe variation of e_source_webdav_get_email_address().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: the newly-allocated copy of #ESourceWebdav:email-address
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_webdav_dup_email_address (ESourceWebdav *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_webdav_get_email_address (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_webdav_set_email_address:
+ * @extension: an #ESourceWebdav
+ * @email_address: the user's email address, or %NULL
+ *
+ * Sets the user's email address which can be passed to a CalDAV server if
+ * the user wishes to receive scheduling messages.
+ *
+ * The internal copy of @email_address is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_webdav_set_email_address (ESourceWebdav *extension,
+                                   const gchar *email_address)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (email_address);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->email_address);
+	extension->priv->email_address = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "email-address");
+}
+
+/**
+ * e_source_webdav_get_ignore_invalid_cert:
+ * @extension: an #ESourceWebdav
+ *
+ * Returns %TRUE if invalid SSL certificates should be ignored.
+ *
+ * This option allows SSL certificates to be accepted even if they have
+ * signed by an unrecognized Certificate Authority.
+ *
+ * Returns: whether invalid SSL certificates should be ignored
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_webdav_get_ignore_invalid_cert (ESourceWebdav *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE);
+
+	return extension->priv->ignore_invalid_cert;
+}
+
+/**
+ * e_source_webdav_set_ignore_invalid_cert:
+ * @extension: an #ESourceWebdav
+ * @ignore_invalid_cert: whether invalid SSL certificates should be ignored
+ *
+ * Sets whether invalid SSL certificates should be ignored.
+ *
+ * This option allows SSL certificates to be accepted even if they have
+ * signed by an unrecognized Certificate Authority.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_webdav_set_ignore_invalid_cert (ESourceWebdav *extension,
+                                         gboolean ignore_invalid_cert)
+{
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	extension->priv->ignore_invalid_cert = ignore_invalid_cert;
+
+	g_object_notify (G_OBJECT (extension), "ignore-invalid-cert");
+}
+
+/**
+ * e_source_webdav_get_resource_path:
+ * @extension: an #ESourceWebdav
+ *
+ * Returns the absolute path to a resource on a WebDAV server.
+ *
+ * Returns: the absolute path to a WebDAV resource
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_webdav_get_resource_path (ESourceWebdav *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	return extension->priv->resource_path;
+}
+
+/**
+ * e_source_webdav_dup_resource_path:
+ * @extension: an #ESourceWebdav
+ *
+ * Thread-safe variation of e_source_webdav_get_resource_path().
+ * Use this function when accessing @extension from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: the newly-allocated copy of #ESourceWebdav:resource-path
+ *
+ * Since: 3.6
+ **/
+gchar *
+e_source_webdav_dup_resource_path (ESourceWebdav *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_webdav_get_resource_path (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_webdav_set_resource_path:
+ * @extension: an #ESourceWebdav
+ * @resource_path: the absolute path to a WebDAV resource, or %NULL
+ *
+ * Sets the absolute path to a resource on a WebDAV server.
+ *
+ * The internal copy of @resource_path is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_webdav_set_resource_path (ESourceWebdav *extension,
+                                   const gchar *resource_path)
+{
+	gchar *duplicate;
+
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (resource_path);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
+
+	g_free (extension->priv->resource_path);
+	extension->priv->resource_path = duplicate;
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "resource-path");
+}
+
+/**
+ * e_source_webdav_dup_soup_uri:
+ * @extension: an #ESourceWebdav
+ *
+ * This is a convenience function which returns a newly-allocated
+ * #SoupURI, its contents assembled from the #ESourceAuthentication
+ * extension, the #ESourceSecurity extension, and @extension itself.
+ * Free the returned #SoupURI with soup_uri_free().
+ *
+ * Returns: a newly-allocated #SoupURI
+ *
+ * Since: 3.6
+ **/
+SoupURI *
+e_source_webdav_dup_soup_uri (ESourceWebdav *extension)
+{
+	SoupURI *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	duplicate = soup_uri_copy (extension->priv->uri);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_webdav_set_soup_uri:
+ * @extension: an #ESourceWebdav
+ * @uri: a #SoupURI
+ *
+ * This is a convenience function which propagates the components of
+ * @uri to the #ESourceAuthentication extension, the #ESourceSecurity
+ * extension, and @extension itself.  (The "query" and "fragment"
+ * components of @uri are ignored.)
+ *
+ * Since: 3.6
+ **/
+void
+e_source_webdav_set_soup_uri (ESourceWebdav *extension,
+                              SoupURI *uri)
+{
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+	g_return_if_fail (SOUP_URI_IS_VALID (uri));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	soup_uri_free (extension->priv->uri);
+	extension->priv->uri = soup_uri_copy (uri);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "soup-uri");
+}
+
diff --git a/libedataserver/e-source-webdav.h b/libedataserver/e-source-webdav.h
new file mode 100644
index 0000000..3871277
--- /dev/null
+++ b/libedataserver/e-source-webdav.h
@@ -0,0 +1,120 @@
+/*
+ * e-source-webdav.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_SOURCE_WEBDAV_H
+#define E_SOURCE_WEBDAV_H
+
+#include <libsoup/soup.h>
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_WEBDAV \
+	(e_source_webdav_get_type ())
+#define E_SOURCE_WEBDAV(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdav))
+#define E_SOURCE_WEBDAV_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_WEBDAV, ESourceWebdavClass))
+#define E_IS_SOURCE_WEBDAV(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_WEBDAV))
+#define E_IS_SOURCE_WEBDAV_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_WEBDAV))
+#define E_SOURCE_WEBDAV_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdavClass))
+
+/**
+ * E_SOURCE_EXTENSION_WEBDAV_BACKEND:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceWebdav.  This is also used as a group name in key files.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_EXTENSION_WEBDAV_BACKEND "WebDAV Backend"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceWebdav ESourceWebdav;
+typedef struct _ESourceWebdavClass ESourceWebdavClass;
+typedef struct _ESourceWebdavPrivate ESourceWebdavPrivate;
+
+/**
+ * ESourceWebdav:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.6
+ **/
+struct _ESourceWebdav {
+	ESourceExtension parent;
+	ESourceWebdavPrivate *priv;
+};
+
+struct _ESourceWebdavClass {
+	ESourceExtensionClass parent_class;
+};
+
+GType		e_source_webdav_get_type	(void) G_GNUC_CONST;
+gboolean	e_source_webdav_get_avoid_ifmatch
+						(ESourceWebdav *extension);
+void		e_source_webdav_set_avoid_ifmatch
+						(ESourceWebdav *extension,
+						 gboolean avoid_ifmatch);
+gboolean	e_source_webdav_get_calendar_auto_schedule
+						(ESourceWebdav *extension);
+void		e_source_webdav_set_calendar_auto_schedule
+						(ESourceWebdav *extension,
+						 gboolean calendar_auto_schedule);
+const gchar *	e_source_webdav_get_display_name
+						(ESourceWebdav *extension);
+gchar *		e_source_webdav_dup_display_name
+						(ESourceWebdav *extension);
+void		e_source_webdav_set_display_name
+						(ESourceWebdav *extension,
+						 const gchar *display_name);
+const gchar *	e_source_webdav_get_email_address
+						(ESourceWebdav *extension);
+gchar *		e_source_webdav_dup_email_address
+						(ESourceWebdav *extension);
+void		e_source_webdav_set_email_address
+						(ESourceWebdav *extension,
+						 const gchar *email_address);
+gboolean	e_source_webdav_get_ignore_invalid_cert
+						(ESourceWebdav *extension);
+void		e_source_webdav_set_ignore_invalid_cert
+						(ESourceWebdav *extension,
+						 gboolean ignore_invalid_cert);
+const gchar *	e_source_webdav_get_resource_path
+						(ESourceWebdav *extension);
+gchar *		e_source_webdav_dup_resource_path
+						(ESourceWebdav *extension);
+void		e_source_webdav_set_resource_path
+						(ESourceWebdav *extension,
+						 const gchar *resource_path);
+SoupURI *	e_source_webdav_dup_soup_uri	(ESourceWebdav *extension);
+void		e_source_webdav_set_soup_uri	(ESourceWebdav *extension,
+						 SoupURI *uri);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_WEBDAV_H */
diff --git a/libedataserver/e-source.c b/libedataserver/e-source.c
index 4b96575..896de21 100644
--- a/libedataserver/e-source.c
+++ b/libedataserver/e-source.c
@@ -1,50 +1,144 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/* e-source.c
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+/*
+ * e-source.c
  *
  * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
+ * 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
- * General Public License for more details.
+ * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
  *
- * Author: Ettore Perazzoli <ettore ximian com>
  */
 
+/**
+ * SECTION: e-source
+ * @include: libedataserver/e-source.h
+ * @short_description: Hierarchical data sources
+ *
+ * An #ESource (or "data source") is a description of a file or network
+ * location where data can be obtained (such as a mail account), or a
+ * description of a resource at that location (such as a mail folder).
+ *
+ * In more concrete terms, it's an interface for a key file.  All such
+ * key files have a main group named [Data Source].  The keys in a
+ * [Data Source] group map to #GObject properties in an #ESource.
+ *
+ * Additional groups in the key file are referred to as "extensions".
+ * #ESourceExtension serves as the base class for writing interfaces
+ * for these additional key file groups.  The keys in one of these
+ * key file groups map to #GObject properties in some custom subclass
+ * of #ESourceExtension which was written specifically for that key
+ * file group.  For example, a key file might include a group named
+ * [Calendar], whose keys map to #GObject properties in an extension
+ * class named #ESourceCalendar.
+ *
+ * Each #ESource contains an internal dictionary of extension objects,
+ * accessible by their key file group name.  e_source_get_extension()
+ * can look up extension objects by name.
+ *
+ * An #ESource is identified by a unique identifier string, or "UID",
+ * which is also the basename of the corresponding key file.  Additional
+ * files related to the #ESource, such as cache files, are usually kept
+ * in a directory named after the UID of the #ESource.  Similarly, the
+ * password for an account described by an #ESource is kept in GNOME
+ * Keyring under the UID of the #ESource.  This makes finding these
+ * additional resources simple.
+ *
+ * Several extensions for common information such as authentication
+ * details are built into libedataserver (#ESourceAuthentication, for
+ * example).  Backend modules may also define their own extensions for
+ * information and settings unique to the backend.  #ESourceExtension
+ * subclasses written for specific backends are generally not available
+ * to applications and shared libraries.  This is by design, to try and
+ * keep backend-specific knowledge from creeping into places it doesn't
+ * belong.
+ **/
+
 #include "e-source.h"
 
 #include <config.h>
 #include <string.h>
-
-#include <libedataserver/e-uid.h>
-#include <libedataserver/e-source-group.h>
+#include <glib/gi18n-lib.h>
+
+/* Private D-Bus classes. */
+#include <e-dbus-source.h>
+
+#include "e-data-server-util.h"
+#include "e-source-extension.h"
+#include "e-uid.h"
+
+/* built-in extension types */
+#include "e-source-address-book.h"
+#include "e-source-alarms.h"
+#include "e-source-authentication.h"
+#include "e-source-autocomplete.h"
+#include "e-source-calendar.h"
+#include "e-source-camel.h"
+#include "e-source-collection.h"
+#include "e-source-goa.h"
+#include "e-source-mail-account.h"
+#include "e-source-mail-composition.h"
+#include "e-source-mail-identity.h"
+#include "e-source-mail-signature.h"
+#include "e-source-mail-submission.h"
+#include "e-source-mail-transport.h"
+#include "e-source-mdn.h"
+#include "e-source-offline.h"
+#include "e-source-openpgp.h"
+#include "e-source-refresh.h"
+#include "e-source-security.h"
+#include "e-source-selectable.h"
+#include "e-source-smime.h"
+#include "e-source-webdav.h"
 
 #define E_SOURCE_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_SOURCE, ESourcePrivate))
 
+/* This forces the GType to be registered in a way that
+ * avoids a "statement with no effect" compiler warning. */
+#define REGISTER_TYPE(type) \
+	(g_type_class_unref (g_type_class_ref (type)))
+
+#define PRIMARY_GROUP_NAME	"Data Source"
+
 struct _ESourcePrivate {
-	ESourceGroup *group;
+	GDBusObject *dbus_object;
+	GMainContext *main_context;
+
+	GSource *changed;
+	GMutex *changed_lock;
 
+	GMutex *property_lock;
+	gchar *display_name;
+	gchar *parent;
 	gchar *uid;
-	gchar *name;
-	gchar *relative_uri;
-	gchar *absolute_uri;
 
-	gboolean readonly;
+	/* The lock guards the key file and hash table. */
 
-	gchar *color_spec;
+	GKeyFile *key_file;
+	GStaticRecMutex lock;
+	GHashTable *extensions;
 
-	GHashTable *properties;
+	gboolean enabled;
+};
+
+enum {
+	PROP_0,
+	PROP_DBUS_OBJECT,
+	PROP_DISPLAY_NAME,
+	PROP_ENABLED,
+	PROP_MAIN_CONTEXT,
+	PROP_PARENT,
+	PROP_REMOVABLE,
+	PROP_UID,
+	PROP_WRITABLE
 };
 
 enum {
@@ -52,1197 +146,1985 @@ enum {
 	LAST_SIGNAL
 };
 
-static guint signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL];
+
+/* Forward Declarations */
+static void	e_source_initable_init		(GInitableIface *interface);
 
-/* Callbacks.  */
+G_DEFINE_TYPE_WITH_CODE (
+	ESource,
+	e_source,
+	G_TYPE_OBJECT,
+	G_IMPLEMENT_INTERFACE (
+		G_TYPE_INITABLE,
+		e_source_initable_init))
 
 static void
-group_weak_notify (ESource *source,
-                   GObject **where_the_object_was)
+source_find_extension_classes_rec (GType parent_type,
+                                   GHashTable *hash_table)
 {
-	source->priv->group = NULL;
+	GType *children;
+	guint n_children, ii;
 
-	g_signal_emit (source, signals[CHANGED], 0);
+	children = g_type_children (parent_type, &n_children);
+
+	for (ii = 0; ii < n_children; ii++) {
+		GType type = children[ii];
+		ESourceExtensionClass *class;
+		gpointer key;
+
+		/* Recurse over the child's children. */
+		source_find_extension_classes_rec (type, hash_table);
+
+		/* Skip abstract types. */
+		if (G_TYPE_IS_ABSTRACT (type))
+			continue;
+
+		class = g_type_class_ref (type);
+		key = (gpointer) class->name;
+
+		if (key != NULL)
+			g_hash_table_insert (hash_table, key, class);
+		else
+			g_type_class_unref (class);
+	}
+
+	g_free (children);
 }
 
-/* GObject methods.  */
+static GHashTable *
+source_find_extension_classes (void)
+{
+	GHashTable *hash_table;
+
+	hash_table = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) NULL,
+		(GDestroyNotify) g_type_class_unref);
 
-G_DEFINE_TYPE (ESource, e_source, G_TYPE_OBJECT)
+	source_find_extension_classes_rec (
+		E_TYPE_SOURCE_EXTENSION, hash_table);
+
+	return hash_table;
+}
 
 static void
-source_finalize (GObject *object)
+source_localized_hack (GKeyFile *key_file,
+                       const gchar *group_name,
+                       const gchar *key,
+                       const gchar *new_value)
 {
-	ESourcePrivate *priv;
+	const gchar * const *language_names;
+	gchar *localized_key;
 
-	priv = E_SOURCE_GET_PRIVATE (object);
+	/* XXX If we're changing a string key that has translations,
+	 *     set both "key" and "key[$CURRENT_LOCALE]" to the new
+	 *     value so g_key_file_get_locale_string() will pick it
+	 *     up.  This is not a perfect solution however.  When a
+	 *     different locale is used the value may revert to its
+	 *     original localized string.  Good enough for now. */
 
-	g_free (priv->uid);
-	g_free (priv->name);
-	g_free (priv->relative_uri);
-	g_free (priv->absolute_uri);
-	g_free (priv->color_spec);
+	language_names = g_get_language_names ();
+	localized_key = g_strdup_printf ("%s[%s]", key, language_names[0]);
 
-	g_hash_table_destroy (priv->properties);
+	if (g_key_file_has_key (key_file, group_name, localized_key, NULL))
+		g_key_file_set_string (
+			key_file, group_name, localized_key, new_value);
 
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (e_source_parent_class)->finalize (object);
+	g_free (localized_key);
 }
 
 static void
-source_dispose (GObject *object)
+source_set_key_file_from_property (GObject *object,
+                                   GParamSpec *pspec,
+                                   GKeyFile *key_file,
+                                   const gchar *group_name)
 {
-	ESourcePrivate *priv;
-
-	priv = E_SOURCE_GET_PRIVATE (object);
+	GValue *pvalue;
+	GValue *svalue;
+	gchar *key;
+
+	pvalue = g_slice_new0 (GValue);
+	g_value_init (pvalue, pspec->value_type);
+	g_object_get_property (object, pspec->name, pvalue);
+
+	svalue = g_slice_new0 (GValue);
+	g_value_init (svalue, G_TYPE_STRING);
+
+	key = e_source_parameter_to_key (pspec->name);
+
+	/* For the most part we can just transform any supported
+	 * property type to a string, with a couple exceptions. */
+
+	/* Transforming a boolean GValue to a string results in
+	 * "TRUE" or "FALSE" (all uppercase), but GKeyFile only
+	 * recognizes "true" or "false" (all lowercase).  So we
+	 * have to use g_key_file_set_boolean(). */
+	if (G_VALUE_HOLDS_BOOLEAN (pvalue)) {
+		gboolean v_boolean = g_value_get_boolean (pvalue);
+		g_key_file_set_boolean (key_file, group_name, key, v_boolean);
+
+	/* String GValues may contain characters that need escaping. */
+	} else if (G_VALUE_HOLDS_STRING (pvalue)) {
+		const gchar *v_string = g_value_get_string (pvalue);
+
+		if (v_string == NULL)
+			v_string = "";
+
+		/* Special case for localized "DisplayName" keys. */
+		source_localized_hack (key_file, group_name, key, v_string);
+		g_key_file_set_string (key_file, group_name, key, v_string);
+
+	/* Transforming an enum GValue to a string results in
+	 * the GEnumValue name.  We want the shorter nickname. */
+	} else if (G_VALUE_HOLDS_ENUM (pvalue)) {
+		GParamSpecEnum *enum_pspec;
+		GEnumClass *enum_class;
+		GEnumValue *enum_value;
+		gint value;
+
+		enum_pspec = G_PARAM_SPEC_ENUM (pspec);
+		enum_class = enum_pspec->enum_class;
+
+		value = g_value_get_enum (pvalue);
+		enum_value = g_enum_get_value (enum_class, value);
+
+		if (enum_value == NULL) {
+			value = enum_pspec->default_value;
+			enum_value = g_enum_get_value (enum_class, value);
+		}
 
-	if (priv->group != NULL) {
-		g_object_weak_unref (G_OBJECT (priv->group), (GWeakNotify) group_weak_notify, object);
-		priv->group = NULL;
+		if (enum_value != NULL)
+			g_key_file_set_string (
+				key_file, group_name, key,
+				enum_value->value_nick);
+
+	} else if (G_VALUE_HOLDS (pvalue, G_TYPE_STRV)) {
+		const gchar **strv = g_value_get_boxed (pvalue);
+		guint length = 0;
+
+		if (strv != NULL)
+			length = g_strv_length ((gchar **) strv);
+		g_key_file_set_string_list (
+			key_file, group_name, key, strv, length);
+
+	/* For GValues holding a GFile object we save the URI. */
+	} else if (G_VALUE_HOLDS (pvalue, G_TYPE_FILE)) {
+		GFile *file = g_value_get_object (pvalue);
+		gchar *uri = NULL;
+
+		if (file != NULL)
+			uri = g_file_get_uri (file);
+		g_key_file_set_string (
+			key_file, group_name, key,
+			(uri != NULL) ? uri : "");
+		g_free (uri);
+
+	} else if (g_value_transform (pvalue, svalue)) {
+		const gchar *value = g_value_get_string (svalue);
+		g_key_file_set_value (key_file, group_name, key, value);
 	}
 
-	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (e_source_parent_class)->dispose (object);
+	g_free (key);
+	g_value_unset (pvalue);
+	g_value_unset (svalue);
+	g_slice_free (GValue, pvalue);
+	g_slice_free (GValue, svalue);
 }
 
-/* Initialization.  */
-
 static void
-e_source_class_init (ESourceClass *class)
+source_set_property_from_key_file (GObject *object,
+                                   GParamSpec *pspec,
+                                   GKeyFile *key_file,
+                                   const gchar *group_name)
 {
-	GObjectClass *object_class;
+	gchar *key;
+	GValue *value;
+	GError *error = NULL;
+
+	value = g_slice_new0 (GValue);
+	key = e_source_parameter_to_key (pspec->name);
+
+	if (G_IS_PARAM_SPEC_CHAR (pspec) ||
+	    G_IS_PARAM_SPEC_UCHAR (pspec) ||
+	    G_IS_PARAM_SPEC_INT (pspec) ||
+	    G_IS_PARAM_SPEC_UINT (pspec) ||
+	    G_IS_PARAM_SPEC_LONG (pspec) ||
+	    G_IS_PARAM_SPEC_ULONG (pspec)) {
+		gint v_int;
+
+		v_int = g_key_file_get_integer (
+			key_file, group_name, key, &error);
+		if (error == NULL) {
+			g_value_init (value, G_TYPE_INT);
+			g_value_set_int (value, v_int);
+		}
 
-	g_type_class_add_private (class, sizeof (ESourcePrivate));
+	} else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) {
+		gboolean v_boolean;
 
-	object_class = G_OBJECT_CLASS (class);
-	object_class->dispose = source_dispose;
-	object_class->finalize = source_finalize;
+		v_boolean = g_key_file_get_boolean (
+			key_file, group_name, key, &error);
+		if (error == NULL) {
+			g_value_init (value, G_TYPE_BOOLEAN);
+			g_value_set_boolean (value, v_boolean);
+		}
 
-	signals[CHANGED] = g_signal_new (
-		"changed",
-		G_OBJECT_CLASS_TYPE (object_class),
-		G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET (ESourceClass, changed),
-		NULL, NULL,
-		g_cclosure_marshal_VOID__VOID,
-		G_TYPE_NONE, 0);
+	} else if (G_IS_PARAM_SPEC_ENUM (pspec)) {
+		gchar *nick;
+
+		nick = g_key_file_get_string (
+			key_file, group_name, key, &error);
+		if (error == NULL) {
+			GParamSpecEnum *enum_pspec;
+			GEnumValue *enum_value;
+
+			enum_pspec = G_PARAM_SPEC_ENUM (pspec);
+			enum_value = g_enum_get_value_by_nick (
+				enum_pspec->enum_class, nick);
+			if (enum_value != NULL) {
+				g_value_init (value, pspec->value_type);
+				g_value_set_enum (value, enum_value->value);
+			}
+			g_free (nick);
+		}
+
+	} else if (G_IS_PARAM_SPEC_FLOAT (pspec) ||
+		   G_IS_PARAM_SPEC_DOUBLE (pspec)) {
+		gdouble v_double;
+
+		v_double = g_key_file_get_double (
+			key_file, group_name, key, &error);
+		if (error == NULL) {
+			g_value_init (value, G_TYPE_DOUBLE);
+			g_value_set_double (value, v_double);
+		}
+
+	} else if (G_IS_PARAM_SPEC_STRING (pspec)) {
+		gchar *v_string;
+
+		/* Get the localized string if present. */
+		v_string = g_key_file_get_locale_string (
+			key_file, group_name, key, NULL, &error);
+		if (error == NULL) {
+			g_value_init (value, G_TYPE_STRING);
+			g_value_take_string (value, v_string);
+		}
+
+	} else if (g_type_is_a (pspec->value_type, G_TYPE_STRV)) {
+		gchar **strv;
+
+		strv = g_key_file_get_string_list (
+			key_file, group_name, key, NULL, &error);
+		if (error == NULL) {
+			g_value_init (value, G_TYPE_STRV);
+			g_value_take_boxed (value, strv);
+		}
+
+	} else if (g_type_is_a (pspec->value_type, G_TYPE_FILE)) {
+		gchar *uri;
+
+		/* Create the GFile from the URI string. */
+		uri = g_key_file_get_locale_string (
+			key_file, group_name, key, NULL, &error);
+		if (error == NULL) {
+			GFile *file = NULL;
+			if (uri != NULL && *uri != '\0')
+				file = g_file_new_for_uri (uri);
+			g_value_init (value, pspec->value_type);
+			g_value_take_object (value, file);
+			g_free (uri);
+		}
+
+	} else {
+		g_warning (
+			"No GKeyFile-to-GValue converter defined "
+			"for type '%s'", G_VALUE_TYPE_NAME (value));
+	}
+
+	/* If a value could not be retrieved from the key
+	 * file, restore the property to its default value. */
+	if (error != NULL) {
+		g_value_init (value, pspec->value_type);
+		g_param_value_set_default (pspec, value);
+		g_error_free (error);
+	}
+
+	if (G_IS_VALUE (value)) {
+		g_object_set_property (object, pspec->name, value);
+		g_value_unset (value);
+	}
+
+	g_slice_free (GValue, value);
+	g_free (key);
 }
 
 static void
-e_source_init (ESource *source)
+source_load_from_key_file (GObject *object,
+                           GKeyFile *key_file,
+                           const gchar *group_name)
 {
-	source->priv = E_SOURCE_GET_PRIVATE (source);
+	GObjectClass *class;
+	GParamSpec **properties;
+	guint n_properties, ii;
 
-	source->priv->properties = g_hash_table_new_full (
-		(GHashFunc) g_str_hash,
-		(GEqualFunc) g_str_equal,
-		(GDestroyNotify) g_free,
-		(GDestroyNotify) g_free);
+	class = G_OBJECT_GET_CLASS (object);
+	properties = g_object_class_list_properties (class, &n_properties);
+
+	g_object_freeze_notify (object);
+
+	for (ii = 0; ii < n_properties; ii++) {
+		if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) {
+			source_set_property_from_key_file (
+				object, properties[ii], key_file, group_name);
+		}
+	}
+
+	g_object_thaw_notify (object);
+
+	g_free (properties);
 }
 
-/* Private methods. */
+static void
+source_save_to_key_file (GObject *object,
+                         GKeyFile *key_file,
+                         const gchar *group_name)
+{
+	GObjectClass *class;
+	GParamSpec **properties;
+	guint n_properties, ii;
+
+	class = G_OBJECT_GET_CLASS (object);
+	properties = g_object_class_list_properties (class, &n_properties);
+
+	for (ii = 0; ii < n_properties; ii++) {
+		if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) {
+			source_set_key_file_from_property (
+				object, properties[ii], key_file, group_name);
+		}
+	}
+
+	g_free (properties);
+}
 
 static gboolean
-set_color_spec (ESource *source,
-                const gchar *color_spec)
+source_parse_dbus_data (ESource *source,
+                        GError **error)
 {
-	ESourcePrivate *priv = source->priv;
-	gboolean do_cmp;
+	GHashTableIter iter;
+	EDBusObject *dbus_object;
+	EDBusSource *dbus_source;
+	GKeyFile *key_file;
+	gpointer group_name;
+	gpointer extension;
+	gchar *data;
+	gboolean success;
 
-	if (color_spec == priv->color_spec)
+	dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
+
+	dbus_source = e_dbus_object_get_source (dbus_object);
+	data = e_dbus_source_dup_data (dbus_source);
+	g_object_unref (dbus_source);
+
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	key_file = source->priv->key_file;
+
+	success = g_key_file_load_from_data (
+		key_file, data, strlen (data),
+		G_KEY_FILE_KEEP_COMMENTS |
+		G_KEY_FILE_KEEP_TRANSLATIONS,
+		error);
+
+	g_free (data);
+	data = NULL;
+
+	if (!success)
 		return FALSE;
 
-	do_cmp = (color_spec != NULL && priv->color_spec != NULL);
-	if (do_cmp && g_ascii_strcasecmp (color_spec, priv->color_spec) == 0)
+	/* Make sure the key file has a [Data Source] group. */
+	if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) {
+		g_set_error (
+			error, G_KEY_FILE_ERROR,
+			G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+			_("Source file is missing a [%s] group"),
+			PRIMARY_GROUP_NAME);
 		return FALSE;
+	}
+
+	/* Load key file values from the [Data Source] group and from
+	 * any other groups for which an extension object has already
+	 * been created.  Note that not all the extension classes may
+	 * be registered at this point, so avoid attempting to create
+	 * new extension objects here.  Extension objects are created
+	 * on-demand in e_source_get_extension(). */
 
-	g_free (priv->color_spec);
-	priv->color_spec = g_strdup (color_spec);
+	source_load_from_key_file (
+		G_OBJECT (source), key_file, PRIMARY_GROUP_NAME);
+
+	g_hash_table_iter_init (&iter, source->priv->extensions);
+	while (g_hash_table_iter_next (&iter, &group_name, &extension))
+		source_load_from_key_file (extension, key_file, group_name);
 
 	return TRUE;
 }
 
-/**
- * e_source_new:
- * @name: a display name for the source
- * @relative_uri: a relative URI for the source
- *
- * Creates a new #ESource instance, and gives it a display name specified
- * by @name and a relative URI specified by @relative_uri.
- *
- * Returns: a new #ESource
- **/
-ESource *
-e_source_new (const gchar *name,
-              const gchar *relative_uri)
+static void
+source_notify_dbus_data_cb (EDBusSource *dbus_source,
+                            GParamSpec *pspec,
+                            ESource *source)
 {
-	ESource *source;
-
-	g_return_val_if_fail (name != NULL, NULL);
-	g_return_val_if_fail (relative_uri != NULL, NULL);
+	GError *error = NULL;
 
-	source = g_object_new (E_TYPE_SOURCE, NULL);
-	source->priv->uid = e_uid_new ();
+	g_static_rec_mutex_lock (&source->priv->lock);
 
-	e_source_set_name (source, name);
-	e_source_set_relative_uri (source, relative_uri);
+	/* Since the source data came from a GKeyFile structure on the
+	 * server-side, this should never fail.  But we'll print error
+	 * messages to the terminal just in case. */
+	if (!source_parse_dbus_data (source, &error)) {
+		g_return_if_fail (error != NULL);
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
 
-	return source;
+	g_static_rec_mutex_unlock (&source->priv->lock);
 }
 
-/**
- * e_source_new_with_absolute_uri:
- * @name: a display name for the source
- * @absolute_uri: a custom absolute URI for the source
- *
- * Creates a new #ESource instance, and gives it a display name specified
- * by @name and a custom absolute URI specified by @abolute_uri.
- *
- * Returns: a new #ESource
- **/
-ESource *
-e_source_new_with_absolute_uri (const gchar *name,
-                                const gchar *absolute_uri)
+static gboolean
+source_idle_changed_cb (gpointer user_data)
 {
-	ESource *source;
+	ESource *source = E_SOURCE (user_data);
 
-	g_return_val_if_fail (name != NULL, NULL);
-	g_return_val_if_fail (absolute_uri != NULL, NULL);
+	g_mutex_lock (source->priv->changed_lock);
+	g_source_unref (source->priv->changed);
+	source->priv->changed = NULL;
+	g_mutex_unlock (source->priv->changed_lock);
 
-	source = g_object_new (E_TYPE_SOURCE, NULL);
-	source->priv->uid = e_uid_new ();
+	g_signal_emit (source, signals[CHANGED], 0);
 
-	e_source_set_name (source, name);
-	e_source_set_absolute_uri (source, absolute_uri);
+	return FALSE;
+}
 
-	return source;
+static void
+source_set_dbus_object (ESource *source,
+                        EDBusObject *dbus_object)
+{
+	/* D-Bus object will be NULL when configuring a new source. */
+	if (dbus_object == NULL)
+		return;
+
+	g_return_if_fail (E_DBUS_IS_OBJECT (dbus_object));
+	g_return_if_fail (source->priv->dbus_object == NULL);
+
+	source->priv->dbus_object = g_object_ref (dbus_object);
 }
 
-/**
- * e_source_new_from_xml_node:
- * @node: a pointer to the XML node to parse
- *
- * Creates a new #ESource instance from the XML specification in @node.
- * If the XML specification is invalid, the function returns %NULL.
- *
- * Returns: a new #ESource, or %NULL
- **/
-ESource *
-e_source_new_from_xml_node (xmlNodePtr node)
+static void
+source_set_main_context (ESource *source,
+                         GMainContext *main_context)
 {
-	ESource *source;
-	xmlChar *uid;
+	g_return_if_fail (source->priv->main_context == NULL);
 
-	uid = xmlGetProp (node, (xmlChar *)"uid");
-	if (uid == NULL)
-		return NULL;
+	source->priv->main_context =
+		(main_context != NULL) ?
+		g_main_context_ref (main_context) :
+		g_main_context_ref_thread_default ();
+}
 
-	source = g_object_new (E_TYPE_SOURCE, NULL);
+static void
+source_set_property (GObject *object,
+                     guint property_id,
+                     const GValue *value,
+                     GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_DBUS_OBJECT:
+			source_set_dbus_object (
+				E_SOURCE (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_DISPLAY_NAME:
+			e_source_set_display_name (
+				E_SOURCE (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_ENABLED:
+			e_source_set_enabled (
+				E_SOURCE (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_MAIN_CONTEXT:
+			source_set_main_context (
+				E_SOURCE (object),
+				g_value_get_boxed (value));
+			return;
+
+		case PROP_PARENT:
+			e_source_set_parent (
+				E_SOURCE (object),
+				g_value_get_string (value));
+			return;
+	}
 
-	source->priv->uid = g_strdup ((gchar *) uid);
-	xmlFree (uid);
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
 
-	if (e_source_update_from_xml_node (source, node, NULL))
-		return source;
+static void
+source_get_property (GObject *object,
+                     guint property_id,
+                     GValue *value,
+                     GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_DBUS_OBJECT:
+			g_value_take_object (
+				value, e_source_ref_dbus_object (
+				E_SOURCE (object)));
+			return;
+
+		case PROP_DISPLAY_NAME:
+			g_value_take_string (
+				value, e_source_dup_display_name (
+				E_SOURCE (object)));
+			return;
+
+		case PROP_ENABLED:
+			g_value_set_boolean (
+				value, e_source_get_enabled (
+				E_SOURCE (object)));
+			return;
+
+		case PROP_MAIN_CONTEXT:
+			g_value_take_boxed (
+				value, e_source_ref_main_context (
+				E_SOURCE (object)));
+			return;
+
+		case PROP_PARENT:
+			g_value_take_string (
+				value, e_source_dup_parent (
+				E_SOURCE (object)));
+			return;
+
+		case PROP_REMOVABLE:
+			g_value_set_boolean (
+				value, e_source_get_removable (
+				E_SOURCE (object)));
+			return;
+
+		case PROP_UID:
+			g_value_take_string (
+				value, e_source_dup_uid (
+				E_SOURCE (object)));
+			return;
+
+		case PROP_WRITABLE:
+			g_value_set_boolean (
+				value, e_source_get_writable (
+				E_SOURCE (object)));
+			return;
+	}
 
-	g_object_unref (source);
-	return NULL;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
 static void
-import_properties (ESource *source,
-                   xmlNodePtr prop_root)
+source_dispose (GObject *object)
 {
-	ESourcePrivate *priv = source->priv;
-	xmlNodePtr prop_node;
+	ESourcePrivate *priv;
 
-	for (prop_node = prop_root->children; prop_node; prop_node = prop_node->next) {
-		xmlChar *name, *value;
+	priv = E_SOURCE_GET_PRIVATE (object);
 
-		if (!prop_node->name || strcmp ((gchar *)prop_node->name, "property"))
-			continue;
+	if (priv->dbus_object != NULL) {
+		EDBusObject *dbus_object;
+		EDBusSource *dbus_source;
+
+		dbus_object = E_DBUS_OBJECT (priv->dbus_object);
+
+		dbus_source = e_dbus_object_get_source (dbus_object);
+		g_signal_handlers_disconnect_matched (
+			dbus_source, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (dbus_source);
 
-		name = xmlGetProp (prop_node, (xmlChar *)"name");
-		value = xmlGetProp (prop_node, (xmlChar *)"value");
+		g_object_unref (priv->dbus_object);
+		priv->dbus_object = NULL;
+	}
 
-		if (name && value)
-			g_hash_table_insert (priv->properties, g_strdup ((gchar *) name), g_strdup ((gchar *) value));
+	if (priv->main_context != NULL) {
+		g_main_context_unref (priv->main_context);
+		priv->main_context = NULL;
+	}
 
-		if (name)
-			xmlFree (name);
-		if (value)
-			xmlFree (value);
+	/* XXX Maybe not necessary to acquire the lock? */
+	g_mutex_lock (priv->changed_lock);
+	if (priv->changed != NULL) {
+		g_source_destroy (priv->changed);
+		g_source_unref (priv->changed);
+		priv->changed = NULL;
 	}
+	g_mutex_unlock (priv->changed_lock);
+
+	g_hash_table_remove_all (priv->extensions);
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_source_parent_class)->dispose (object);
 }
 
-typedef struct
+static void
+source_finalize (GObject *object)
 {
-	gboolean equal;
-	GHashTable *table2;
-} hash_compare_data;
+	ESourcePrivate *priv;
+
+	priv = E_SOURCE_GET_PRIVATE (object);
+
+	g_mutex_free (priv->changed_lock);
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->display_name);
+	g_free (priv->parent);
+	g_free (priv->uid);
+
+	g_key_file_free (priv->key_file);
+	g_static_rec_mutex_free (&priv->lock);
+	g_hash_table_destroy (priv->extensions);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_parent_class)->finalize (object);
+}
 
 static void
-compare_str_hash (gpointer key,
-                  gpointer value,
-                  hash_compare_data *cd)
+source_notify (GObject *object,
+               GParamSpec *pspec)
 {
-	gpointer value2 = g_hash_table_lookup (cd->table2, key);
-	if (value2 == NULL || g_str_equal (value, value2) == FALSE)
-		cd->equal = FALSE;
+	if ((pspec->flags & E_SOURCE_PARAM_SETTING) != 0)
+		e_source_changed (E_SOURCE (object));
 }
 
 static gboolean
-compare_str_hashes (GHashTable *table1,
-                    GHashTable *table2)
+source_remove_sync (ESource *source,
+                    GCancellable *cancellable,
+                    GError **error)
 {
-	hash_compare_data cd;
+	EDBusObject *dbus_object;
+	EDBusSourceRemovable *dbus_source;
+	gboolean success;
+
+	dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
 
-	if (g_hash_table_size (table1) != g_hash_table_size (table2))
+	dbus_source = e_dbus_object_get_source_removable (dbus_object);
+
+	if (dbus_source == NULL) {
+		g_set_error (
+			error, G_IO_ERROR,
+			G_IO_ERROR_PERMISSION_DENIED,
+			_("Data source '%s' is not removable"),
+			e_source_get_display_name (source));
 		return FALSE;
+	}
+
+	success = e_dbus_source_removable_call_remove_sync (
+		dbus_source, cancellable, error);
+
+	g_object_unref (dbus_source);
 
-	cd.equal = TRUE;
-	cd.table2 = table2;
-	g_hash_table_foreach (table1, (GHFunc) compare_str_hash, &cd);
-	return cd.equal;
+	return success;
 }
 
-/**
- * e_source_update_from_xml_node:
- * @source: an #ESource.
- * @node: a pointer to the XML node to parse
- * @changed_return: return location for change confirmation, or %NULL
- *
- * Update the #ESource attributes from @node.  If @changed_return is
- * non-%NULL, it will be set to %TRUE if any attributes were actually
- * changed in the course of the update.  This will also emit the
- * #ESource::changed signal if any attributes were actually changed.
- *
- * Returns: %TRUE if the data in @node was recognized and parsed into
- * acceptable values for @source, %FALSE otherwise
- **/
-gboolean
-e_source_update_from_xml_node (ESource *source,
-                               xmlNodePtr node,
-                               gboolean *changed_return)
+/* Helper for source_remove() */
+static void
+source_remove_thread (GSimpleAsyncResult *simple,
+                      GObject *object,
+                      GCancellable *cancellable)
 {
-	xmlChar *name;
-	xmlChar *relative_uri;
-	xmlChar *absolute_uri;
-	xmlChar *color_spec;
-	xmlChar *color;
-	gboolean retval = FALSE;
-	gboolean changed = FALSE;
-
-	name = xmlGetProp (node, (xmlChar *)"name");
-	relative_uri = xmlGetProp (node, (xmlChar *)"relative_uri");
-	absolute_uri = xmlGetProp (node, (xmlChar *)"uri");
-	color_spec = xmlGetProp (node, (xmlChar *)"color_spec");
-	color = xmlGetProp (node, (xmlChar *)"color");  /* obsolete */
-
-	if (name == NULL || (relative_uri == NULL && absolute_uri == NULL))
-		goto done;
-
-	if (color_spec != NULL && color != NULL)
-		goto done;
-
-	if (source->priv->name == NULL
-	    || strcmp ((gchar *) name, source->priv->name) != 0
-	    || (source->priv->relative_uri == NULL && relative_uri != NULL)
-	    || (source->priv->relative_uri != NULL && relative_uri == NULL)
-	    || (relative_uri && source->priv->relative_uri && strcmp ((gchar *) relative_uri, source->priv->relative_uri) != 0)) {
-		gchar *abs_uri = NULL;
-
-		g_free (source->priv->name);
-		source->priv->name = g_strdup ((gchar *) name);
-
-		if (source->priv->group) {
-			abs_uri = e_source_build_absolute_uri (source);
-		}
+	GError *error = NULL;
 
-		if (abs_uri && source->priv->absolute_uri && g_str_equal (abs_uri, source->priv->absolute_uri)) {
-			/* reset the absolute uri to NULL to be regenerated when asked for,
-			 * but only when it was generated also before */
-			g_free (source->priv->absolute_uri);
-			source->priv->absolute_uri = NULL;
-		} else if (source->priv->absolute_uri &&
-			   source->priv->relative_uri &&
-			   g_str_has_suffix (source->priv->absolute_uri, source->priv->relative_uri)) {
-			gchar *tmp = source->priv->absolute_uri;
+	e_source_remove_sync (E_SOURCE (object), cancellable, &error);
 
-			tmp[strlen (tmp) - strlen (source->priv->relative_uri)] = 0;
-			source->priv->absolute_uri = g_strconcat (tmp, (gchar *) relative_uri, NULL);
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
 
-			g_free (tmp);
-		}
+static void
+source_remove (ESource *source,
+               GCancellable *cancellable,
+               GAsyncReadyCallback callback,
+               gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
 
-		g_free (abs_uri);
+	simple = g_simple_async_result_new (
+		G_OBJECT (source), callback, user_data, source_remove);
 
-		g_free (source->priv->relative_uri);
-		source->priv->relative_uri = g_strdup ((gchar *) relative_uri);
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-		changed = TRUE;
-	}
+	g_simple_async_result_run_in_thread (
+		simple, source_remove_thread,
+		G_PRIORITY_DEFAULT, cancellable);
 
-	if (absolute_uri != NULL) {
-		g_free (source->priv->absolute_uri);
+	g_object_unref (simple);
+}
 
-		if (relative_uri && g_str_equal ((const gchar *) relative_uri, "system") &&
-		    (g_str_has_prefix ((const gchar *) absolute_uri, "file:") || g_str_equal ((const gchar *) absolute_uri, "local:/system")))
-			source->priv->absolute_uri = g_strdup ("local:system");
-		else
-			source->priv->absolute_uri = g_strdup ((gchar *) absolute_uri);
-		changed = TRUE;
-	}
+static gboolean
+source_remove_finish (ESource *source,
+                      GAsyncResult *result,
+                      GError **error)
+{
+	GSimpleAsyncResult *simple;
 
-	if (color == NULL) {
-		/* It is okay for color_spec to be NULL. */
-		changed |= set_color_spec (source, (gchar *) color_spec);
-	} else {
-		gchar buffer[8];
-		g_snprintf (buffer, sizeof (buffer), "#%s", color);
-		changed |= set_color_spec (source, buffer);
-	}
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (source), source_remove), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static gboolean
+source_write_sync (ESource *source,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+	EDBusObject *dbus_object;
+	EDBusSourceWritable *dbus_source;
+	gboolean success;
+	gchar *data;
 
-	if (g_hash_table_size (source->priv->properties) && !node->children) {
-		g_hash_table_destroy (source->priv->properties);
-		source->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
-								  g_free, g_free);
-		changed = TRUE;
+	dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
+
+	dbus_source = e_dbus_object_get_source_writable (dbus_object);
+
+	if (dbus_source == NULL) {
+		g_set_error (
+			error, G_IO_ERROR,
+			G_IO_ERROR_PERMISSION_DENIED,
+			_("Data source '%s' is not writable"),
+			e_source_get_display_name (source));
+		return FALSE;
 	}
 
-	for (node = node->children; node; node = node->next) {
-		if (!node->name)
-			continue;
+	data = e_source_to_string (source, NULL);
+
+	success = e_dbus_source_writable_call_write_sync (
+		dbus_source, data, cancellable, error);
+
+	g_free (data);
+
+	g_object_unref (dbus_source);
+
+	return success;
+}
+
+/* Helper for source_write() */
+static void
+source_write_thread (GSimpleAsyncResult *simple,
+                     GObject *object,
+                     GCancellable *cancellable)
+{
+	GError *error = NULL;
+
+	e_source_write_sync (E_SOURCE (object), cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+static void
+source_write (ESource *source,
+              GCancellable *cancellable,
+              GAsyncReadyCallback callback,
+              gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (source), callback, user_data, source_write);
+
+	g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_write_thread,
+		G_PRIORITY_DEFAULT, cancellable);
+
+	g_object_unref (simple);
+}
 
-		if (!strcmp ((gchar *)node->name, "properties")) {
-			GHashTable *temp = source->priv->properties;
-			source->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
-									  g_free, g_free);
-			import_properties (source, node);
-			if (!compare_str_hashes (temp, source->priv->properties))
-				changed = TRUE;
-			g_hash_table_destroy (temp);
-			break;
+static gboolean
+source_write_finish (ESource *source,
+                     GAsyncResult *result,
+                     GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (source), source_write), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static gboolean
+source_initable_init (GInitable *initable,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+	ESource *source;
+	gboolean success = TRUE;
+
+	source = E_SOURCE (initable);
+
+	/* The D-Bus object has the unique identifier (UID). */
+	if (source->priv->dbus_object != NULL) {
+		EDBusObject *dbus_object;
+		EDBusSource *dbus_source;
+
+		dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
+
+		/* An EDBusObject lacking an EDBusSource
+		 * interface indicates a programmer error. */
+		dbus_source = e_dbus_object_get_source (dbus_object);
+		g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
+
+		/* Allow authentication prompts for a data source
+		 * when a new client-side proxy object is created.
+		 * The thought being if you cancel an authentication
+		 * prompt you won't be bothered again until you start
+		 * (or restart) a new E-D-S client app.
+		 *
+		 * Failure here is non-fatal, ignore errors.
+		 *
+		 * XXX Only GDBusProxy objects may call this.  Sources
+		 *     created server-side can't invoke remote methods.
+		 */
+		if (G_IS_DBUS_PROXY (dbus_source))
+			e_dbus_source_call_allow_auth_prompt_sync (
+				dbus_source, cancellable, NULL);
+
+		/* The UID never changes, so we can cache a copy. */
+		source->priv->uid = e_dbus_source_dup_uid (dbus_source);
+
+		g_signal_connect (
+			dbus_source, "notify::data",
+			G_CALLBACK (source_notify_dbus_data_cb), source);
+
+		success = source_parse_dbus_data (source, error);
+
+		/* Avoid a spurious "changed" emission. */
+		g_mutex_lock (source->priv->changed_lock);
+		if (source->priv->changed != NULL) {
+			g_source_destroy (source->priv->changed);
+			g_source_unref (source->priv->changed);
+			source->priv->changed = NULL;
 		}
+		g_mutex_unlock (source->priv->changed_lock);
+
+		g_object_unref (dbus_source);
+
+	/* No D-Bus object implies we're configuring a new source,
+	 * so generate a new unique identifier (UID) for it. */
+	} else {
+		source->priv->uid = e_uid_new ();
 	}
 
-	retval = TRUE;
+	return success;
+}
+
+static void
+e_source_class_init (ESourceClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (ESourcePrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_set_property;
+	object_class->get_property = source_get_property;
+	object_class->dispose = source_dispose;
+	object_class->finalize = source_finalize;
+	object_class->notify = source_notify;
+
+	class->remove_sync = source_remove_sync;
+	class->remove = source_remove;
+	class->remove_finish = source_remove_finish;
+	class->write_sync = source_write_sync;
+	class->write = source_write;
+	class->write_finish = source_write_finish;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_DBUS_OBJECT,
+		g_param_spec_object (
+			"dbus-object",
+			"D-Bus Object",
+			"The D-Bus object for the data source",
+			E_DBUS_TYPE_OBJECT,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_DISPLAY_NAME,
+		g_param_spec_string (
+			"display-name",
+			"Display Name",
+			"The human-readable name of the data source",
+			_("Unnamed"),
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ENABLED,
+		g_param_spec_boolean (
+			"enabled",
+			"Enabled",
+			"Whether the data source is enabled",
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_MAIN_CONTEXT,
+		g_param_spec_boxed (
+			"main-context",
+			"Main Context",
+			"The GMainContext used for signal emissions",
+			G_TYPE_MAIN_CONTEXT,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PARENT,
+		g_param_spec_string (
+			"parent",
+			"Parent",
+			"The unique identity of the parent data source",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_REMOVABLE,
+		g_param_spec_boolean (
+			"removable",
+			"Removable",
+			"Whether the data source is removable",
+			FALSE,
+			G_PARAM_READABLE |
+			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 data source is writable",
+			FALSE,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * ESource::changed:
+	 * @source: the #ESource that received the signal
+	 *
+	 * The ::changed signal is emitted when a property in @source or
+	 * one of its extension objects changes.  A common use for this
+	 * signal is to notify a #GtkTreeModel containing data collected
+	 * from #ESource<!-- -->s that it needs to update a row.
+	 **/
+	signals[CHANGED] = g_signal_new (
+		"changed",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+		G_STRUCT_OFFSET (ESourceClass, changed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	/* Register built-in ESourceExtension types. */
+	REGISTER_TYPE (E_TYPE_SOURCE_ADDRESS_BOOK);
+	REGISTER_TYPE (E_TYPE_SOURCE_ALARMS);
+	REGISTER_TYPE (E_TYPE_SOURCE_AUTHENTICATION);
+	REGISTER_TYPE (E_TYPE_SOURCE_AUTOCOMPLETE);
+	REGISTER_TYPE (E_TYPE_SOURCE_CALENDAR);
+	REGISTER_TYPE (E_TYPE_SOURCE_COLLECTION);
+	REGISTER_TYPE (E_TYPE_SOURCE_GOA);
+	REGISTER_TYPE (E_TYPE_SOURCE_MAIL_ACCOUNT);
+	REGISTER_TYPE (E_TYPE_SOURCE_MAIL_COMPOSITION);
+	REGISTER_TYPE (E_TYPE_SOURCE_MAIL_IDENTITY);
+	REGISTER_TYPE (E_TYPE_SOURCE_MAIL_SIGNATURE);
+	REGISTER_TYPE (E_TYPE_SOURCE_MAIL_SUBMISSION);
+	REGISTER_TYPE (E_TYPE_SOURCE_MAIL_TRANSPORT);
+	REGISTER_TYPE (E_TYPE_SOURCE_MDN);
+	REGISTER_TYPE (E_TYPE_SOURCE_MEMO_LIST);
+	REGISTER_TYPE (E_TYPE_SOURCE_OFFLINE);
+	REGISTER_TYPE (E_TYPE_SOURCE_OPENPGP);
+	REGISTER_TYPE (E_TYPE_SOURCE_REFRESH);
+	REGISTER_TYPE (E_TYPE_SOURCE_SECURITY);
+	REGISTER_TYPE (E_TYPE_SOURCE_SELECTABLE);
+	REGISTER_TYPE (E_TYPE_SOURCE_SMIME);
+	REGISTER_TYPE (E_TYPE_SOURCE_TASK_LIST);
+	REGISTER_TYPE (E_TYPE_SOURCE_WEBDAV);
+
+	e_source_camel_register_types ();
+}
+
+static void
+e_source_initable_init (GInitableIface *interface)
+{
+	interface->init = source_initable_init;
+}
 
-done:
-	if (changed)
-		g_signal_emit (source, signals[CHANGED], 0);
+static void
+e_source_init (ESource *source)
+{
+	GHashTable *extensions;
 
-	if (changed_return != NULL)
-		*changed_return = changed;
+	extensions = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_object_unref);
 
-	if (name != NULL)
-		xmlFree (name);
-	if (relative_uri != NULL)
-		xmlFree (relative_uri);
-	if (absolute_uri != NULL)
-		xmlFree (absolute_uri);
-	if (color_spec != NULL)
-		xmlFree (color_spec);
-	if (color != NULL)
-		xmlFree (color);
+	source->priv = E_SOURCE_GET_PRIVATE (source);
+	source->priv->changed_lock = g_mutex_new ();
+	source->priv->property_lock = g_mutex_new ();
+	source->priv->key_file = g_key_file_new ();
+	source->priv->extensions = extensions;
 
-	return retval;
+	g_static_rec_mutex_init (&source->priv->lock);
 }
 
 /**
- * e_source_uid_from_xml_node:
- * @node: a pointer to an XML node
+ * e_source_new:
+ * @dbus_object: a #GDBusObject or %NULL
+ * @main_context: a #GMainContext or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a new #ESource instance.
+ *
+ * The #ESource::changed signal will be emitted from @main_context if given,
+ * or else from the thread-default #GMainContext at the time this function is
+ * called.
  *
- * Assuming that @node is a valid #ESource specification, retrieve the
- * source's unique identifier string from it.  Free the returned string
- * with g_free().
+ * The only time the function should be called outside of #ESourceRegistry
+ * is to create a so-called "scratch" #ESource for editing in a Properties
+ * window or an account setup assistant.
  *
- * Returns: the unique ID of the source specified by @node,
- *          or %NULL if @node is not a valid specification
+ * FIXME: Elaborate on scratch sources.
+ *
+ * Returns: a new #ESource, or %NULL on error
+ *
+ * Since: 3.6
  **/
-gchar *
-e_source_uid_from_xml_node (xmlNodePtr node)
+ESource *
+e_source_new (GDBusObject *dbus_object,
+              GMainContext *main_context,
+              GError **error)
 {
-	xmlChar *prop;
-	gchar *uid = NULL;
-
-	prop = xmlGetProp (node, (xmlChar *) "uid");
-
-	if (prop != NULL) {
-		uid = g_strdup ((gchar *) prop);
-		xmlFree (prop);
-	}
-
-	return uid;
+	if (dbus_object != NULL)
+		g_return_val_if_fail (E_DBUS_IS_OBJECT (dbus_object), NULL);
+
+	return g_initable_new (
+		E_TYPE_SOURCE, NULL, error,
+		"dbus-object", dbus_object,
+		"main-context", main_context,
+		NULL);
 }
 
 /**
- * e_source_build_absolute_uri:
+ * e_source_hash:
  * @source: an #ESource
  *
- * Builds an absolute URI string using the base URI of the #ESourceGroup
- * to which @source belongs, and its own relative URI.  This function
- * ignores any custom absolute URIs set with e_source_set_absolute_uri().
- * Free the returned string with g_free().
+ * Generates a hash value for @source.  This function is intended for
+ * easily hashing an #ESource to add to a #GHashTable or similar data
+ * structure.
+ *
+ * Returns: a hash value for @source.
  *
- * Returns: a newly-allocated absolute URI string
+ * Since: 3.6
  **/
-gchar *
-e_source_build_absolute_uri (ESource *source)
+guint
+e_source_hash (ESource *source)
 {
-	const gchar *base_uri_str;
-	gchar *uri_str;
-
-	g_return_val_if_fail (source->priv->group != NULL, NULL);
-
-	base_uri_str = e_source_group_peek_base_uri (source->priv->group);
-
-	/* If last character in base URI is a slash, just concat the
-	 * strings.  We don't want to compress e.g. the trailing ://
-	 * in a protocol specification Note: Do not use
-	 * G_DIR_SEPARATOR or g_build_filename() when manipulating
-	 * URIs. URIs use normal ("forward") slashes also on Windows.
-	 */
-	if (*base_uri_str && *(base_uri_str + strlen (base_uri_str) - 1) == '/')
-		uri_str = g_strconcat (base_uri_str, source->priv->relative_uri, NULL);
-	else {
-		if (source->priv->relative_uri != NULL)
-			uri_str = g_strconcat (base_uri_str, g_str_equal (base_uri_str, "local:") ? "" : "/", source->priv->relative_uri,
-				       NULL);
-		else
-			uri_str = g_strdup (base_uri_str);
-	}
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), 0);
 
-	return uri_str;
+	uid = e_source_get_uid (source);
+
+	return g_str_hash (uid);
 }
 
 /**
- * e_source_set_group:
- * @source: an #ESource
- * @group: an #ESourceGroup
- *
- * If the read-only flag for @source is set, the function does nothing.
+ * e_source_equal:
+ * @source1: the first #ESource
+ * @source2: the second #ESource
  *
- * Otherwise, sets the group membership for @source.
+ * Checks two #ESource instances for equality.  #ESource instances are
+ * equal if their unique identifier strings are equal.
  *
- * <note>
- *   <para>
- *     If you want to add an #ESource to an #ESourceGroup, use
- *     e_source_group_add_source().  This function only notifies
- *     @source of its group membership, but makes no effort to
- *     verify that membership with @group.
- *   </para>
- * </note>
+ * Returns: %TRUE if @source1 and @source2 are equal
  *
- * This will emit the #ESource::changed signal if the group membership
- * actually changed.
+ * Since: 3.6
  **/
-void
-e_source_set_group (ESource *source,
-                    ESourceGroup *group)
+gboolean
+e_source_equal (ESource *source1,
+                ESource *source2)
 {
-	g_return_if_fail (E_IS_SOURCE (source));
-	g_return_if_fail (group == NULL || E_IS_SOURCE_GROUP (group));
-
-	if (source->priv->readonly)
-		return;
+	const gchar *uid1, *uid2;
 
-	if (source->priv->group == group)
-		return;
+	g_return_val_if_fail (E_IS_SOURCE (source1), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (source2), FALSE);
 
-	if (source->priv->group != NULL)
-		g_object_weak_unref (
-			G_OBJECT (source->priv->group),
-			(GWeakNotify) group_weak_notify, source);
+	if (source1 == source2)
+		return TRUE;
 
-	source->priv->group = group;
-	if (group != NULL)
-		g_object_weak_ref (
-			G_OBJECT (group), (GWeakNotify)
-			group_weak_notify, source);
+	uid1 = e_source_get_uid (source1);
+	uid2 = e_source_get_uid (source2);
 
-	g_signal_emit (source, signals[CHANGED], 0);
+	return g_str_equal (uid1, uid2);
 }
 
 /**
- * e_source_set_name:
+ * e_source_changed:
  * @source: an #ESource
- * @name: a display name
  *
- * If the read-only flag for @source is set, the function does nothing.
+ * Emits the #ESource::changed signal from an idle callback in
+ * @source's #ESource:main-context.
  *
- * Otherwise, sets the display name for @source.
+ * This function is primarily intended for use by #ESourceExtension
+ * when emitting a #GObject::notify signal on one of its properties.
  *
- * This will emit the #ESource::changed signal if the display name
- * actually changed.
+ * Since: 3.6
  **/
 void
-e_source_set_name (ESource *source,
-                   const gchar *name)
+e_source_changed (ESource *source)
 {
 	g_return_if_fail (E_IS_SOURCE (source));
-	g_return_if_fail (name != NULL);
-
-	if (source->priv->readonly)
-		return;
 
-	if (source->priv->name != NULL &&
-	    strcmp (source->priv->name, name) == 0)
-		return;
+	g_mutex_lock (source->priv->changed_lock);
+	if (source->priv->changed == NULL) {
+		source->priv->changed = g_idle_source_new ();
+		g_source_set_callback (
+			source->priv->changed,
+			source_idle_changed_cb,
+			source, (GDestroyNotify) NULL);
+		g_source_attach (
+			source->priv->changed,
+			source->priv->main_context);
+	}
+	g_mutex_unlock (source->priv->changed_lock);
+}
 
-	g_free (source->priv->name);
-	source->priv->name = g_strdup (name);
+/**
+ * e_source_get_uid:
+ * @source: an #ESource
+ *
+ * Returns the unique identifier string for @source.
+ *
+ * Returns: the UID for @source
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_get_uid (ESource *source)
+{
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	g_signal_emit (source, signals[CHANGED], 0);
+	return source->priv->uid;
 }
 
 /**
- * e_source_set_relative_uri:
+ * e_source_dup_uid:
  * @source: an #ESource
- * @relative_uri: a relative URI string
  *
- * If the read-only flag for @source is set, the function does nothing.
+ * Thread-safe variation of e_source_get_uid().
+ * Use this function when accessing @source from a worker thread.
  *
- * Otherwise, sets the relative URI for @source.  If @source is a member
- * of an #ESourceGroup and has not been given a custom absolute URI, the
- * function also generates a new absolute URI for @source.
+ * The returned string should be freed with g_free() when no longer needed.
  *
- * This will emit the #ESource::changed signal if the relative URI
- * actually changed.
+ * Returns: a newly-allocated copy of #ESource:uid
+ *
+ * Since: 3.6
  **/
-void
-e_source_set_relative_uri (ESource *source,
-                           const gchar *relative_uri)
+gchar *
+e_source_dup_uid (ESource *source)
 {
-	gchar *absolute_uri, *old_abs_uri = NULL;
+	const gchar *protected;
+	gchar *duplicate;
 
-	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	if (source->priv->readonly)
-		return;
+	/* Perhaps we don't need to lock the mutex since
+	 * this is a read-only property but it can't hurt. */
 
-	if (source->priv->relative_uri == relative_uri ||
-	    (source->priv->relative_uri && relative_uri && g_str_equal (source->priv->relative_uri, relative_uri)))
-		return;
+	g_mutex_lock (source->priv->property_lock);
 
-	if (source->priv->group)
-		old_abs_uri = e_source_build_absolute_uri (source);
+	protected = e_source_get_uid (source);
+	duplicate = g_strdup (protected);
 
-	g_free (source->priv->relative_uri);
-	source->priv->relative_uri = g_strdup (relative_uri);
+	g_mutex_unlock (source->priv->property_lock);
 
-	/* reset the absolute uri, if it's a generated one */
-	if (source->priv->absolute_uri &&
-	    (!old_abs_uri || g_str_equal (source->priv->absolute_uri, old_abs_uri)) &&
-	    (absolute_uri = e_source_build_absolute_uri (source))) {
-		g_free (source->priv->absolute_uri);
-		source->priv->absolute_uri = absolute_uri;
-	}
+	return duplicate;
+}
 
-	g_free (old_abs_uri);
+/**
+ * e_source_get_parent:
+ * @source: an #ESource
+ *
+ * Returns the unique identifier string of the parent #ESource.
+ *
+ * Returns: the UID of the parent #ESource
+ *
+ * Since: 3.6
+ **/
+const gchar *
+e_source_get_parent (ESource *source)
+{
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	g_signal_emit (source, signals[CHANGED], 0);
+	return source->priv->parent;
 }
 
 /**
- * e_source_set_absolute_uri:
+ * e_source_dup_parent:
  * @source: an #ESource
- * @absolute_uri: an absolute URI string, or %NULL
  *
- * Sets a custom absolute URI for @source.  If @absolute_uri is %NULL, the
- * custom absolute URI is cleared and @source will fall back to its relative
- * URI plus the base URI of its containing #ESourceGroup.
+ * Thread-safe variation of e_source_get_parent().
+ * Use this function when accessing @source from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESource:parent
  *
- * This will emit the #ESource::changed signal if the custom absolute URI
- * actually changed.
+ * Since: 3.6
  **/
-void
-e_source_set_absolute_uri (ESource *source,
-                           const gchar *absolute_uri)
+gchar *
+e_source_dup_parent (ESource *source)
 {
-	g_return_if_fail (E_IS_SOURCE (source));
+	const gchar *protected;
+	gchar *duplicate;
 
-	if ((absolute_uri == source->priv->absolute_uri && absolute_uri == NULL)
-	    || (absolute_uri && source->priv->absolute_uri && !strcmp (source->priv->absolute_uri, absolute_uri)))
-		return;
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	g_free (source->priv->absolute_uri);
-	source->priv->absolute_uri = g_strdup (absolute_uri);
+	g_mutex_lock (source->priv->property_lock);
 
-	g_signal_emit (source, signals[CHANGED], 0);
+	protected = e_source_get_parent (source);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (source->priv->property_lock);
+
+	return duplicate;
 }
 
 /**
- * e_source_set_readonly:
+ * e_source_set_parent:
  * @source: an #ESource
- * @readonly: a read-only flag
+ * @parent: the UID of the parent #ESource
  *
- * Sets @source as being read-only (%TRUE) or writable (%FALSE).
- * A read-only #ESource ignores attempts to change its display name,
- * #ESourceGroup, relative URI or color.
+ * Identifies the parent of @source by its unique identifier string.
+ * This can only be set prior to adding @source to an #ESourceRegistry.
  *
- * This will emit the #ESource::changed signal if the read-only state
- * actually changed.
+ * The internal copy of #ESource:parent is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.6
  **/
 void
-e_source_set_readonly (ESource *source,
-                       gboolean readonly)
+e_source_set_parent (ESource *source,
+                     const gchar *parent)
 {
+	gchar *duplicate;
+
 	g_return_if_fail (E_IS_SOURCE (source));
 
-	if (source->priv->readonly == readonly)
-		return;
+	g_mutex_lock (source->priv->property_lock);
+
+	/* Strip leading and trailing whitespace, and
+	 * set to NULL if the resulting string is empty. */
+	duplicate = g_strdup (parent);
+	if (duplicate != NULL) {
+		g_strstrip (duplicate);
+		if (*duplicate == '\0') {
+			g_free (duplicate);
+			duplicate = NULL;
+		}
+	}
 
-	source->priv->readonly = readonly;
+	g_free (source->priv->parent);
+	source->priv->parent = duplicate;
 
-	g_signal_emit (source, signals[CHANGED], 0);
+	g_mutex_unlock (source->priv->property_lock);
 
+	g_object_notify (G_OBJECT (source), "parent");
 }
 
 /**
- * e_source_set_color_spec:
+ * e_source_get_enabled:
  * @source: an #ESource
- * @color_spec: a string specifying the color
  *
- * Store a textual representation of a color in @source.  The @color_spec
- * string should be parsable by #gdk_color_parse(), or %NULL to unset the
- * color in @source.
+ * Returns %TRUE if @source is enabled.
+ *
+ * An application should try to honor this setting if at all possible,
+ * even if it does not provide a way to change the setting through its
+ * user interface.  Disabled data sources should generally be hidden.
  *
- * This will emit the #ESource::changed signal if the color representation
- * actually changed.
+ * Returns: whether @source is enabled
  *
- * Since: 1.10
+ * Since: 3.6
+ **/
+gboolean
+e_source_get_enabled (ESource *source)
+{
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+	return source->priv->enabled;
+}
+
+/**
+ * e_source_set_enabled:
+ * @source: an #ESource
+ * @enabled: whether to enable @source
+ *
+ * Enables or disables @source.
+ *
+ * An application should try to honor this setting if at all possible,
+ * even if it does not provide a way to change the setting through its
+ * user interface.  Disabled data sources should generally be hidden.
+ *
+ * Since: 3.6
  **/
 void
-e_source_set_color_spec (ESource *source,
-                         const gchar *color_spec)
+e_source_set_enabled (ESource *source,
+                      gboolean enabled)
 {
 	g_return_if_fail (E_IS_SOURCE (source));
 
-	if (!source->priv->readonly && set_color_spec (source, color_spec))
-		g_signal_emit (source, signals[CHANGED], 0);
+	if (enabled == source->priv->enabled)
+		return;
+
+	source->priv->enabled = enabled;
+
+	g_object_notify (G_OBJECT (source), "enabled");
 }
 
 /**
- * e_source_peek_group:
+ * e_source_get_writable:
  * @source: an #ESource
  *
- * Returns the #ESourceGroup to which @source belongs, or %NULL if it
- * does not belong to a group.
+ * Returns whether the D-Bus service will accept changes to @source.
+ * If @source is not writable, calls to e_source_write() will fail.
+ *
+ * Returns: whether @source is writable
  *
- * Returns: (transfer none): the #ESourceGroup to which the source belongs
+ * Since: 3.6
  **/
-ESourceGroup *
-e_source_peek_group (ESource *source)
+gboolean
+e_source_get_writable (ESource *source)
 {
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	EDBusObject *dbus_object;
+	EDBusSourceWritable *dbus_source;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
 
-	return source->priv->group;
+	dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
+	dbus_source = e_dbus_object_peek_source_writable (dbus_object);
+
+	return (dbus_source != NULL);
 }
 
 /**
- * e_source_peek_uid:
+ * e_source_get_removable:
  * @source: an #ESource
  *
- * Returns the unique identifier string for @source.
+ * Returns whether the D-Bus service will allow @source to be removed.
+ * If @source is not writable, calls to e_source_registry_remove_source()
+ * will fail.
  *
- * Returns: the source's unique ID
+ * Returns: whether @source is removable
+ *
+ * Since: 3.6
  **/
-const gchar *
-e_source_peek_uid (ESource *source)
+gboolean
+e_source_get_removable (ESource *source)
 {
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	EDBusObject *dbus_object;
+	EDBusSourceRemovable *dbus_source;
 
-	return source->priv->uid;
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+	dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
+	dbus_source = e_dbus_object_peek_source_removable (dbus_object);
+
+	return (dbus_source != NULL);
 }
 
 /**
- * e_source_peek_name:
+ * e_source_get_extension:
  * @source: an #ESource
+ * @extension_name: an extension name
+ *
+ * Returns an instance of some #ESourceExtension subclass which registered
+ * itself under @extension_name.  If no such instance exists within @source,
+ * one will be created.  It is the caller's responsibility to know which
+ * subclass is being returned.
  *
- * Returns the display name for @source.
+ * If you just want to test for the existence of an extension within @source
+ * without creating it, use e_source_has_extension().
  *
- * Returns: the source's display name
+ * Extension instances are owned by their #ESource and should not be
+ * referenced directly.  Instead, reference the #ESource instance and
+ * use this function to fetch the extension instance as needed.
+ *
+ * Returns: an instance of some #ESourceExtension subclass
+ *
+ * Since: 3.6
  **/
-const gchar *
-e_source_peek_name (ESource *source)
+gpointer
+e_source_get_extension (ESource *source,
+                        const gchar *extension_name)
 {
+	ESourceExtension *extension;
+	GHashTable *hash_table;
+	GTypeClass *class;
+
 	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	g_return_val_if_fail (extension_name != NULL, NULL);
+
+	g_static_rec_mutex_lock (&source->priv->lock);
+
+	/* Check if we already have the extension. */
+	extension = g_hash_table_lookup (
+		source->priv->extensions, extension_name);
+	if (extension != NULL)
+		goto exit;
+
+	/* Find all subclasses of ESourceExtensionClass. */
+	hash_table = source_find_extension_classes ();
+	class = g_hash_table_lookup (hash_table, extension_name);
+
+	/* Create a new instance of the appropriate GType. */
+	if (class != NULL) {
+		extension = g_object_new (
+			G_TYPE_FROM_CLASS (class),
+			"source", source, NULL);
+		source_load_from_key_file (
+			G_OBJECT (extension),
+			source->priv->key_file,
+			extension_name);
+		g_hash_table_insert (
+			source->priv->extensions,
+			g_strdup (extension_name), extension);
+	} else {
+		/* XXX Tie this into a debug setting for ESources. */
+#ifdef DEBUG
+		g_critical (
+			"No registered GType for ESource "
+			"extension '%s'", extension_name);
+#endif
+	}
+
+	g_hash_table_destroy (hash_table);
+
+exit:
+	g_static_rec_mutex_unlock (&source->priv->lock);
 
-	return source->priv->name;
+	return extension;
 }
 
 /**
- * e_source_peek_relative_uri:
+ * e_source_has_extension:
  * @source: an #ESource
+ * @extension_name: an extension name
  *
- * Returns the relative URI for @source.
+ * Checks whether @source has an #ESourceExtension with the given name.
  *
- * Returns: the source's relative URI
+ * Returns: %TRUE if @source has such an extension, %FALSE if not
+ *
+ * Since: 3.6
  **/
-const gchar *
-e_source_peek_relative_uri (ESource *source)
+gboolean
+e_source_has_extension (ESource *source,
+                        const gchar *extension_name)
 {
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	ESourceExtension *extension;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (extension_name != NULL, FALSE);
+
+	g_static_rec_mutex_lock (&source->priv->lock);
+
+	/* Two cases to check for, either one is good enough:
+	 * 1) Our internal GKeyFile has a group named 'extension_name'.
+	 * 2) Our 'extensions' table has an entry for 'extension_name'.
+	 *
+	 * We have to check both data structures in case a new extension
+	 * not present in the GKeyFile was instantiated, but we have not
+	 * yet updated our internal GKeyFile.  A common occurrence when
+	 * editing a brand new data source.
+	 *
+	 * When checking the GKeyFile we want to actually fetch the
+	 * extension with e_source_get_extension() to make sure it's
+	 * a registered extension name and not just an arbitrary key
+	 * file group name. */
+
+	if (g_key_file_has_group (source->priv->key_file, extension_name)) {
+		extension = e_source_get_extension (source, extension_name);
+	} else {
+		GHashTable *hash_table = source->priv->extensions;
+		extension = g_hash_table_lookup (hash_table, extension_name);
+	}
 
-	return source->priv->relative_uri;
+	g_static_rec_mutex_unlock (&source->priv->lock);
+
+	return (extension != NULL);
 }
 
 /**
- * e_source_peek_absolute_uri:
+ * e_source_ref_dbus_object:
  * @source: an #ESource
  *
- * Returns the absolute URI for @source if it has one, or else %NULL if
- * it has only a relative URI.  e_source_get_uri() may be more convenient.
+ * Returns the #GDBusObject that was passed to e_source_new().
+ *
+ * The returned #GDBusObject is referenced for thread-safety and must be
+ * unreferenced with g_object_unref() when finished with it.
  *
- * Returns: the source's own absolute URI, or %NULL
+ * Returns: the #GDBusObject for @source, or %NULL
+ *
+ * Since: 3.6
  **/
-const gchar *
-e_source_peek_absolute_uri (ESource *source)
+GDBusObject *
+e_source_ref_dbus_object (ESource *source)
 {
 	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	return source->priv->absolute_uri;
+	if (source->priv->dbus_object == NULL)
+		return NULL;
+
+	return g_object_ref (source->priv->dbus_object);
 }
 
 /**
- * e_source_peek_color_spec:
+ * e_source_ref_main_context:
  * @source: an #ESource
  *
- * Return the textual representation of the color for @source, or %NULL if it
- * has none.  The returned string should be parsable by #gdk_color_parse().
+ * Returns the #GMainContext from which #ESource::changed signals are
+ * emitted.
  *
- * Returns: a string specifying the color
+ * The returned #GMainContext is referenced for thread-safety and must be
+ * unreferenced with g_main_context_unref() when finished with it.
  *
- * Since: 1.10
+ * Returns: the #GMainContext for signal emissions
+ *
+ * Since: 3.6
  **/
-const gchar *
-e_source_peek_color_spec (ESource *source)
+GMainContext *
+e_source_ref_main_context (ESource *source)
 {
 	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	return source->priv->color_spec;
+	return g_main_context_ref (source->priv->main_context);
 }
 
 /**
- * e_source_get_readonly:
+ * e_source_get_display_name:
  * @source: an #ESource
  *
- * Returns the read-only flag for @source.
+ * Returns the display name for @source.  Use the display name to
+ * represent the #ESource in a user interface.
  *
- * Returns: %TRUE if the source is read-only, %FALSE if it's writable
+ * Returns: the display name for @source
+ *
+ * Since: 3.6
  **/
-gboolean
-e_source_get_readonly (ESource *source)
+const gchar *
+e_source_get_display_name (ESource *source)
 {
-	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	return source->priv->readonly;
+	return source->priv->display_name;
 }
 
 /**
- * e_source_get_uri:
+ * e_source_dup_display_name:
  * @source: an #ESource
  *
- * Returns a newly-allocated copy of an absolute URI for @source.  If
- * @source has no absolute URI of its own, the URI is constructed from
- * the base URI of its #ESourceGroup and its relative URI.  Free the
- * returned string with g_free().
+ * Thread-safe variation of e_source_get_display_name().
+ * Use this function when accessing @source from a worker thread.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
  *
- * Returns: a newly-allocated absolute URI string
+ * Returns: a newly-allocated copy of #ESource:display-name
+ *
+ * Since: 3.6
  **/
 gchar *
-e_source_get_uri (ESource *source)
+e_source_dup_display_name (ESource *source)
 {
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	const gchar *protected;
+	gchar *duplicate;
 
-	if (source->priv->group == NULL) {
-		if (source->priv->absolute_uri != NULL)
-			return g_strdup (source->priv->absolute_uri);
-
-		g_warning ("e_source_get_uri () called on source with no absolute URI!");
-		return NULL;
-	}
-	else if (source->priv->absolute_uri != NULL) /* source->priv->group != NULL */
-		return g_strdup (source->priv->absolute_uri);
-	else
-		return e_source_build_absolute_uri (source);
-}
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-static void
-property_dump_cb (const xmlChar *key,
-                  const xmlChar *value,
-                  xmlNodePtr root)
-{
-	xmlNodePtr node;
+	g_mutex_lock (source->priv->property_lock);
 
-	node = xmlNewChild (root, NULL, (xmlChar *)"property", NULL);
-	xmlSetProp (node, (xmlChar *)"name", key);
-	xmlSetProp (node, (xmlChar *)"value", value);
-}
+	protected = e_source_get_display_name (source);
+	duplicate = g_strdup (protected);
 
-static xmlNodePtr
-dump_common_to_xml_node (ESource *source,
-                         xmlNodePtr parent_node)
-{
-	ESourcePrivate *priv;
-	xmlNodePtr node;
-	const gchar *abs_uri = NULL, *relative_uri = NULL;
-
-	priv = source->priv;
-
-	if (parent_node)
-		node = xmlNewChild (parent_node, NULL, (xmlChar *)"source", NULL);
-	else
-		node = xmlNewNode (NULL, (xmlChar *)"source");
-
-	xmlSetProp (node, (xmlChar *)"uid", (xmlChar *)e_source_get_uid (source));
-	xmlSetProp (node, (xmlChar *)"name", (xmlChar *)e_source_get_display_name (source));
-	abs_uri = e_source_peek_absolute_uri (source);
-	/* do not store absolute uris for local:system sources */
-	relative_uri = e_source_peek_relative_uri (source);
-	if (abs_uri && !(relative_uri && g_str_equal (relative_uri, "system") &&
-		    (g_str_has_prefix (abs_uri, "file:") || g_str_has_prefix (abs_uri, "local:"))))
-		xmlSetProp (node, (xmlChar *)"uri", (xmlChar *)abs_uri);
-	if (relative_uri)
-		xmlSetProp (node, (xmlChar *)"relative_uri", (xmlChar *)relative_uri);
-
-	if (priv->color_spec != NULL)
-		xmlSetProp (node, (xmlChar *)"color_spec", (xmlChar *)priv->color_spec);
-
-	if (g_hash_table_size (priv->properties) != 0) {
-		xmlNodePtr properties_node;
-
-		properties_node = xmlNewChild (node, NULL, (xmlChar *)"properties", NULL);
-		g_hash_table_foreach (priv->properties, (GHFunc) property_dump_cb, properties_node);
-	}
+	g_mutex_unlock (source->priv->property_lock);
 
-	return node;
+	return duplicate;
 }
 
 /**
- * e_source_dump_to_xml_node:
+ * e_source_set_display_name:
  * @source: an #ESource
- * @parent_node: location to add XML data
+ * @display_name: a display name
+ *
+ * Sets the display name for @source.  The @display_name argument must be a
+ * valid UTF-8 string.  Use the display name to represent the #ESource in a
+ * user interface.
  *
- * Converts @source to an <structname>xmlNode</structname> structure
- * and adds it as a child of @parent_node.
+ * The internal copy of @display_name is automatically stripped of leading
+ * and trailing whitespace.
+ *
+ * Since: 3.6
  **/
 void
-e_source_dump_to_xml_node (ESource *source,
-                           xmlNodePtr parent_node)
+e_source_set_display_name (ESource *source,
+                           const gchar *display_name)
 {
 	g_return_if_fail (E_IS_SOURCE (source));
+	g_return_if_fail (display_name != NULL);
+	g_return_if_fail (g_utf8_validate (display_name, -1, NULL));
+
+	g_mutex_lock (source->priv->property_lock);
+
+	g_free (source->priv->display_name);
+	source->priv->display_name = g_strdup (display_name);
+
+	/* Strip leading and trailing whitespace. */
+	g_strstrip (source->priv->display_name);
 
-	dump_common_to_xml_node (source, parent_node);
+	g_mutex_unlock (source->priv->property_lock);
+
+	g_object_notify (G_OBJECT (source), "display-name");
 }
 
 /**
- * e_source_to_standalone_xml:
- * @source: an #ESource
+ * e_source_compare_by_display_name:
+ * @source1: the first #ESource
+ * @source2: the second #ESource
  *
- * Converts @source to an XML string for permanent storage.
- * Free the returned string with g_free().
+ * Compares two #ESource instances by their display names.  Useful for
+ * ordering sources in a user interface.
+ *
+ * Returns: a negative value if @source1 compares before @source2, zero if
+ *          they compare equal, or a positive value if @source1 compares
+ *          after @source2
  *
- * Returns: a newly-allocated XML string
+ * Since: 3.6
  **/
-gchar *
-e_source_to_standalone_xml (ESource *source)
+gint
+e_source_compare_by_display_name (ESource *source1,
+                                  ESource *source2)
 {
-	xmlDocPtr doc;
-	xmlNodePtr node;
-	xmlChar *xml_buffer;
-	gchar *returned_buffer;
-	gint xml_buffer_size;
-	gchar *uri;
-
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
-
-	doc = xmlNewDoc ((xmlChar *)"1.0");
-	node = dump_common_to_xml_node (source, NULL);
+	const gchar *display_name1;
+	const gchar *display_name2;
 
-	xmlDocSetRootElement (doc, node);
+	display_name1 = e_source_get_display_name (source1);
+	display_name2 = e_source_get_display_name (source2);
 
-	uri = e_source_get_uri (source);
-	xmlSetProp (node, (xmlChar *)"uri", (xmlChar *)uri);
-	g_free (uri);
-
-	xmlDocDumpMemory (doc, &xml_buffer, &xml_buffer_size);
-	xmlFreeDoc (doc);
-
-	returned_buffer = g_malloc (xml_buffer_size + 1);
-	memcpy (returned_buffer, xml_buffer, xml_buffer_size);
-	returned_buffer[xml_buffer_size] = '\0';
-	xmlFree (xml_buffer);
-
-	return returned_buffer;
+	return g_utf8_collate (display_name1, display_name2);
 }
 
 /**
- * e_source_equal:
- * @a: an #ESource
- * @b: another #ESource
+ * e_source_to_string:
+ * @source: an #ESource
+ * @length: return location for the length of the returned string, or %NULL
  *
- * Compares if @a is equivalent to @b.
+ * Outputs the current contents of @source as a key file string.
+ * Free the returned string with g_free().
  *
- * Returns: %TRUE if @a is equivalent to @b, %FALSE otherwise
+ * Returns: a newly-allocated string
  *
- * Since: 2.24
+ * Since: 3.6
  **/
-gboolean
-e_source_equal (ESource *a,
-                ESource *b)
+gchar *
+e_source_to_string (ESource *source,
+                    gsize *length)
 {
-	g_return_val_if_fail (E_IS_SOURCE (a), FALSE);
-	g_return_val_if_fail (E_IS_SOURCE (b), FALSE);
-
-	#define ONLY_ONE_NULL(aa, bb) (((aa) == NULL && (bb) != NULL) || ((aa) != NULL && (bb) == NULL))
-
-	/* Compare source stuff */
-	if (a->priv->uid
-	 && b->priv->uid
-	 && g_ascii_strcasecmp (a->priv->uid, b->priv->uid))
-		return FALSE;
+	GHashTableIter iter;
+	GKeyFile *key_file;
+	gpointer group_name;
+	gpointer extension;
+	gchar *data;
 
-	if (a->priv->name
-	 && b->priv->name
-	 && g_ascii_strcasecmp (a->priv->name, b->priv->name))
-		return FALSE;
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
 
-	if (a->priv->relative_uri
-	 && b->priv->relative_uri
-	 && g_ascii_strcasecmp (a->priv->relative_uri, b->priv->relative_uri))
-		return FALSE;
+	g_static_rec_mutex_lock (&source->priv->lock);
 
-	if (a->priv->absolute_uri
-	 && b->priv->absolute_uri
-	 && g_ascii_strcasecmp (a->priv->absolute_uri, b->priv->absolute_uri))
-		return FALSE;
+	key_file = source->priv->key_file;
 
-	if ((a->priv->color_spec
-	 && b->priv->color_spec
-	 && g_ascii_strcasecmp (a->priv->color_spec, b->priv->color_spec)) ||
-	 (ONLY_ONE_NULL (a->priv->color_spec, b->priv->color_spec)))
-		return FALSE;
+	source_save_to_key_file (
+		G_OBJECT (source), key_file, PRIMARY_GROUP_NAME);
 
-	if (a->priv->readonly != b->priv->readonly)
-		return FALSE;
+	g_hash_table_iter_init (&iter, source->priv->extensions);
+	while (g_hash_table_iter_next (&iter, &group_name, &extension))
+		source_save_to_key_file (extension, key_file, group_name);
 
-	if (!compare_str_hashes (a->priv->properties, b->priv->properties))
-		return FALSE;
+	data = g_key_file_to_data (key_file, length, NULL);
 
-	#undef ONLY_ONE_NULL
+	g_static_rec_mutex_unlock (&source->priv->lock);
 
-	return TRUE;
+	return data;
 }
 
 /**
- * e_source_xmlstr_equal:
- * @a: an XML representation of an #ESource
- * @b: an XML representation of another #ESource
+ * e_source_parameter_to_key:
+ * @param_name: a #GParamSpec name
  *
- * Compares if @a is equivalent to @b.
+ * Converts a #GParamSpec name (e.g. "foo-bar" or "foo_bar")
+ * to "CamelCase" for use as a #GKeyFile key (e.g. "FooBar").
  *
- * Returns: %TRUE if @a is equivalent to @b, %FALSE otherwise
+ * This function is made public only to aid in account migration.
+ * Applications should not need to use this.
  *
- * Since: 2.24
+ * Since: 3.6
  **/
-gboolean
-e_source_xmlstr_equal (const gchar *a,
-                       const gchar *b)
+gchar *
+e_source_parameter_to_key (const gchar *param_name)
 {
-	ESource *srca, *srcb;
-	gboolean retval;
+	gboolean uppercase = TRUE;
+	gchar *key, *cp;
+	gint ii;
 
-	srca = e_source_new_from_standalone_xml (a);
-	srcb = e_source_new_from_standalone_xml (b);
+	g_return_val_if_fail (param_name != NULL, NULL);
 
-	retval = e_source_equal (srca, srcb);
+	key = cp = g_malloc0 (strlen (param_name) + 1);
 
-	g_object_unref (srca);
-	g_object_unref (srcb);
+	for (ii = 0; param_name[ii] != '\0'; ii++) {
+		if (g_ascii_isalnum (param_name[ii]) && uppercase) {
+			*cp++ = g_ascii_toupper (param_name[ii]);
+			uppercase = FALSE;
+		} else if (param_name[ii] == '-' || param_name[ii] == '_')
+			uppercase = TRUE;
+		else
+			*cp++ = param_name[ii];
+	}
 
-	return retval;
+	return key;
 }
 
 /**
- * e_source_new_from_standalone_xml:
- * @xml: an XML representation of an #ESource
+ * e_source_remove_sync:
+ * @source: the #ESource to be removed
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
  *
- * Constructs an #ESource instance from an XML string representation,
- * probably generated by e_source_to_standalone_xml().
+ * Requests the D-Bus service to delete the key files for @source and all of
+ * its descendants and broadcast their removal to all clients.  If an error
+ * occurs, the functon will set @error and return %FALSE.
  *
- * Returns: a new #ESource
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
  **/
-ESource *
-e_source_new_from_standalone_xml (const gchar *xml)
+gboolean
+e_source_remove_sync (ESource *source,
+                      GCancellable *cancellable,
+                      GError **error)
 {
-	xmlDocPtr doc;
-	xmlNodePtr root;
-	ESource *source;
+	ESourceClass *class;
 
-	doc = xmlParseDoc ((xmlChar *) xml);
-	if (doc == NULL)
-		return NULL;
-
-	root = doc->children;
-	if (strcmp ((gchar *)root->name, "source") != 0)
-		return NULL;
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
 
-	source = e_source_new_from_xml_node (root);
-	xmlFreeDoc (doc);
+	class = E_SOURCE_GET_CLASS (source);
+	g_return_val_if_fail (class->remove_sync != NULL, FALSE);
 
-	return source;
+	return class->remove_sync (source, cancellable, error);
 }
 
 /**
- * e_source_get_property:
- * @source: an #ESource
- * @property_name: a custom property name
+ * e_source_remove:
+ * @source: the #ESource to be removed
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously requests the D-Bus service to delete the key files for
+ * @source all of its descendants and broadcast their removal to all clients.
  *
- * Looks up the value of a custom #ESource property.  If no such
- * property name exists in @source, the function returns %NULL.
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_remove_finish() to get the result of the operation.
  *
- * Returns: the property value, or %NULL
+ * Since: 3.6
  **/
-const gchar *
-e_source_get_property (ESource *source,
-                       const gchar *property_name)
+void
+e_source_remove (ESource *source,
+                 GCancellable *cancellable,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
 {
-	const gchar *property_value;
+	ESourceClass *class;
 
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
-	g_return_val_if_fail (property_name != NULL, NULL);
+	g_return_if_fail (E_IS_SOURCE (source));
 
-	property_value = g_hash_table_lookup (
-		source->priv->properties, property_name);
+	class = E_SOURCE_GET_CLASS (source);
+	g_return_if_fail (class->remove != NULL);
 
-	return property_value;
+	class->remove (source, cancellable, callback, user_data);
 }
 
 /**
- * e_source_get_duped_property:
- * @source: an #ESource
- * @property_name: a custom property name
+ * e_source_remove_finish:
+ * @source: the #ESource to be removed
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
  *
- * Looks up the value of a custom #ESource property and returns a
- * newly-allocated copy of the value.  If no such property name exists
- * in @source, the function returns %NULL.  Free the returned value
- * with g_free().
+ * Finishes the operation started with e_source_remove().  If an
+ * error occured, the function will set @error and return %FALSE.
  *
- * Returns: a newly-allocated copy of the property value, or %NULL
+ * Returns: %TRUE on success, %FALSE of failure
  *
- * Since: 1.12
+ * Since: 3.6
  **/
-gchar *
-e_source_get_duped_property (ESource *source,
-                             const gchar *property_name)
+gboolean
+e_source_remove_finish (ESource *source,
+                        GAsyncResult *result,
+                        GError **error)
 {
-	const gchar *property_value;
+	ESourceClass *class;
 
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
-	g_return_val_if_fail (property_name != NULL, NULL);
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
 
-	property_value = e_source_get_property (source, property_name);
+	class = E_SOURCE_GET_CLASS (source);
+	g_return_val_if_fail (class->remove_finish != NULL, FALSE);
 
-	return g_strdup (property_value);
+	return class->remove_finish (source, result, error);
 }
 
 /**
- * e_source_set_property:
- * @source: an #ESource
- * @property_name: a custom property name
- * @property_value: (allow-none): a new value for the property, or %NULL
+ * e_source_write_sync:
+ * @source: a writable #ESource
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Submits the current contents of @source to the D-Bus service to be
+ * written to disk and broadcast to other clients.  This can only be
+ * called on #ESource:writable data sources.
  *
- * Create a new custom #ESource property or replaces an existing one.  If
- * @property_value is %NULL, the property is removed from @source.  This
- * will also emit a #ESource::changed signal.
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
  **/
-void
-e_source_set_property (ESource *source,
-                       const gchar *property_name,
-                       const gchar *property_value)
+gboolean
+e_source_write_sync (ESource *source,
+                     GCancellable *cancellable,
+                     GError **error)
 {
-	const gchar *old_value;
-
-	g_return_if_fail (E_IS_SOURCE (source));
-	g_return_if_fail (property_name != NULL);
-
-	old_value = g_hash_table_lookup (source->priv->properties, property_name);
+	ESourceClass *class;
 
-	if (g_strcmp0 (old_value, property_value) == 0)
-		return;
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
 
-	if (property_value != NULL)
-		g_hash_table_replace (
-			source->priv->properties,
-			g_strdup (property_name),
-			g_strdup (property_value));
-	else
-		g_hash_table_remove (
-			source->priv->properties, property_name);
+	class = E_SOURCE_GET_CLASS (source);
+	g_return_val_if_fail (class->write_sync != NULL, FALSE);
 
-	g_signal_emit (source, signals[CHANGED], 0);
+	return class->write_sync (source, cancellable, error);
 }
 
 /**
- * e_source_foreach_property:
- * @source: an #ESource
- * @func: (scope call): the function to call for each property
- * @user_data: user data to pass to the function
+ * e_source_write:
+ * @source: a writable #ESource
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
  *
- * Calls the given function for each property in @source.  The function
- * is passed the name and value of each property, and the given @user_data
- * argument.  The properties may not be modified while iterating over them.
+ * Asynchronously submits the current contents of @source to the D-Bus
+ * service to be written to disk and broadcast to other clients.  This
+ * can only be called on #ESource:writable data sources.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_write_finish() to get the result of the operation.
+ *
+ * Since: 3.6
  **/
 void
-e_source_foreach_property (ESource *source,
-                           GHFunc func,
-                           gpointer user_data)
+e_source_write (ESource *source,
+                GCancellable *cancellable,
+                GAsyncReadyCallback callback,
+                gpointer user_data)
 {
+	ESourceClass *class;
+
 	g_return_if_fail (E_IS_SOURCE (source));
-	g_return_if_fail (func != NULL);
 
-	g_hash_table_foreach (source->priv->properties, func, user_data);
-}
+	class = E_SOURCE_GET_CLASS (source);
+	g_return_if_fail (class->write != NULL);
 
-static void
-copy_property (const gchar *key,
-               const gchar *value,
-               ESource *new_source)
-{
-	e_source_set_property (new_source, key, value);
+	class->write (source, cancellable, callback, user_data);
 }
 
 /**
- * e_source_copy:
- * @source: an #ESource
+ * e_source_write_finish:
+ * @source: a writable #ESource
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_write().  If an
+ * error occurred, the function will set @error and return %FALSE.
  *
- * Creates a new #ESource instance from @source, such that passing @source
- * and the newly created instance to e_source_equal() would return %TRUE.
+ * Returns: %TRUE on success, %FALSE on failure
  *
- * Returns: (transfer full): a newly-created #ESource
+ * Since: 3.6
  **/
-ESource *
-e_source_copy (ESource *source)
+gboolean
+e_source_write_finish (ESource *source,
+                       GAsyncResult *result,
+                       GError **error)
 {
-	ESource *new_source;
-
-	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	ESourceClass *class;
 
-	new_source = g_object_new (E_TYPE_SOURCE, NULL);
-	new_source->priv->uid = g_strdup (e_source_get_uid (source));
-
-	e_source_set_name (new_source, e_source_get_display_name (source));
-
-	new_source->priv->color_spec =
-		g_strdup (source->priv->color_spec);
-	new_source->priv->absolute_uri =
-		g_strdup (e_source_peek_absolute_uri (source));
-	new_source->priv->relative_uri =
-		g_strdup (e_source_peek_relative_uri (source));
+	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
 
-	e_source_foreach_property (source, (GHFunc) copy_property, new_source);
+	class = E_SOURCE_GET_CLASS (source);
+	g_return_val_if_fail (class->write_finish != NULL, FALSE);
 
-	return new_source;
+	return class->write_finish (source, result, error);
 }
 
diff --git a/libedataserver/e-source.h b/libedataserver/e-source.h
index 8e0db20..df2b520 100644
--- a/libedataserver/e-source.h
+++ b/libedataserver/e-source.h
@@ -1,30 +1,25 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/* e-source.h
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+/*
+ * e-source.h
  *
  * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU Lesser General Public
- * License as published by the Free Software Foundation.
+ * 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
- * General Public License for more details.
+ * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
  *
- * Author: Ettore Perazzoli <ettore ximian com>
  */
 
 #ifndef E_SOURCE_H
 #define E_SOURCE_H
 
-#include <glib-object.h>
-#include <libxml/tree.h>
+#include <gio/gio.h>
 
 /* Standard GObject macros */
 #define E_TYPE_SOURCE \
@@ -45,20 +40,30 @@
 	(G_TYPE_INSTANCE_GET_CLASS \
 	((obj), E_TYPE_SOURCE, ESourceClass))
 
+/**
+ * E_SOURCE_PARAM_SETTING:
+ *
+ * Extends #GParamFlags to indicate the #GObject property is associated
+ * with a key file value.  Use this flag when installing #GObject properties
+ * in #ESourceExtension subclasses.
+ *
+ * Since: 3.6
+ **/
+#define E_SOURCE_PARAM_SETTING (1 << G_PARAM_USER_SHIFT)
+
 G_BEGIN_DECLS
 
 typedef struct _ESource ESource;
 typedef struct _ESourceClass ESourceClass;
 typedef struct _ESourcePrivate ESourcePrivate;
 
-/* Avoids a cyclic dependency. */
-struct _ESourceGroup;
-
 /**
  * ESource:
  *
  * Contains only private data that should be read and manipulated using the
  * functions below.
+ *
+ * Since: 3.6
  **/
 struct _ESource {
 	GObject parent;
@@ -70,64 +75,88 @@ struct _ESourceClass {
 
 	/* Signals */
 	void		(*changed)		(ESource *source);
+
+	/* Methods */
+	gboolean	(*remove_sync)		(ESource *source,
+						 GCancellable *cancellable,
+						 GError **error);
+	void		(*remove)		(ESource *source,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+	gboolean	(*remove_finish)	(ESource *source,
+						 GAsyncResult *result,
+						 GError **error);
+	gboolean	(*write_sync)		(ESource *source,
+						 GCancellable *cancellable,
+						 GError **error);
+	void		(*write)		(ESource *source,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+	gboolean	(*write_finish)		(ESource *source,
+						 GAsyncResult *result,
+						 GError **error);
+
+	/* Reserved slots. */
+	gpointer reserved[16];
 };
 
 GType		e_source_get_type		(void) G_GNUC_CONST;
-ESource *	e_source_new			(const gchar *name,
-						 const gchar *relative_uri);
-ESource *	e_source_new_with_absolute_uri	(const gchar *name,
-						 const gchar *absolute_uri);
-ESource *	e_source_new_from_xml_node	(xmlNodePtr node);
-ESource *	e_source_new_from_standalone_xml
-						(const gchar *xml);
-ESource *	e_source_copy			(ESource *source);
-gboolean	e_source_update_from_xml_node	(ESource *source,
-						 xmlNodePtr node,
-						 gboolean *changed_return);
-gchar *		e_source_uid_from_xml_node	(xmlNodePtr node);
-void		e_source_set_group		(ESource *source,
-						 struct _ESourceGroup *group);
-void		e_source_set_name		(ESource *source,
-						 const gchar *name);
-void		e_source_set_relative_uri	(ESource *source,
-						 const gchar *relative_uri);
-void		e_source_set_absolute_uri	(ESource *source,
-						 const gchar *absolute_uri);
-void		e_source_set_color_spec		(ESource *source,
-						 const gchar *color_spec);
-void		e_source_set_readonly		(ESource *source,
-						 gboolean readonly);
-struct _ESourceGroup *
-		e_source_peek_group		(ESource *source);
-const gchar *	e_source_peek_uid		(ESource *source);
-const gchar *	e_source_peek_name		(ESource *source);
-const gchar *	e_source_peek_relative_uri	(ESource *source);
-const gchar *	e_source_peek_absolute_uri	(ESource *source);
-const gchar *	e_source_peek_color_spec	(ESource *source);
-gboolean	e_source_get_readonly		(ESource *source);
-gchar *		e_source_get_uri		(ESource *source);
-void		e_source_dump_to_xml_node	(ESource *source,
-						 xmlNodePtr parent_node);
-gchar *		e_source_to_standalone_xml	(ESource *source);
-const gchar *	e_source_get_property		(ESource *source,
-						 const gchar *property_name);
-void		e_source_set_property		(ESource *source,
-						 const gchar *property_name,
-						 const gchar *property_value);
-void		e_source_foreach_property	(ESource *source,
-						 GHFunc func,
+ESource *	e_source_new			(GDBusObject *dbus_object,
+						 GMainContext *main_context,
+						 GError **error);
+guint		e_source_hash			(ESource *source);
+gboolean	e_source_equal			(ESource *source1,
+						 ESource *source2);
+void		e_source_changed		(ESource *source);
+const gchar *	e_source_get_uid		(ESource *source);
+gchar *		e_source_dup_uid		(ESource *source);
+const gchar *	e_source_get_parent		(ESource *source);
+gchar *		e_source_dup_parent		(ESource *source);
+void		e_source_set_parent		(ESource *source,
+						 const gchar *parent);
+gboolean	e_source_get_enabled		(ESource *source);
+void		e_source_set_enabled		(ESource *source,
+						 gboolean enabled);
+gboolean	e_source_get_writable		(ESource *source);
+gboolean	e_source_get_removable		(ESource *source);
+gpointer	e_source_get_extension		(ESource *source,
+						 const gchar *extension_name);
+gboolean	e_source_has_extension		(ESource *source,
+						 const gchar *extension_name);
+GDBusObject *	e_source_ref_dbus_object	(ESource *source);
+GMainContext *	e_source_ref_main_context	(ESource *source);
+const gchar *	e_source_get_display_name	(ESource *source);
+gchar *		e_source_dup_display_name	(ESource *source);
+void		e_source_set_display_name	(ESource *source,
+						 const gchar *display_name);
+gint		e_source_compare_by_display_name
+						(ESource *source1,
+						 ESource *source2);
+gchar *		e_source_to_string		(ESource *source,
+						 gsize *length);
+gchar *		e_source_parameter_to_key	(const gchar *param_name);
+gboolean	e_source_remove_sync		(ESource *source,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_source_remove			(ESource *source,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
 						 gpointer user_data);
-gchar *		e_source_get_duped_property	(ESource *source,
-						 const gchar *property_name);
-gchar *		e_source_build_absolute_uri	(ESource *source);
-gboolean	e_source_equal			(ESource *a,
-						 ESource *b);
-gboolean	e_source_xmlstr_equal		(const gchar *a,
-						 const gchar *b);
-
-/* New names to be used in the upcoming ESource rewrite. */
-#define e_source_get_uid		e_source_peek_uid
-#define e_source_get_display_name	e_source_peek_name
+gboolean	e_source_remove_finish		(ESource *source,
+						 GAsyncResult *result,
+						 GError **error);
+gboolean	e_source_write_sync		(ESource *source,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_source_write			(ESource *source,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_source_write_finish		(ESource *source,
+						 GAsyncResult *result,
+						 GError **error);
 
 G_END_DECLS
 
diff --git a/libedataserver/e-system-source.c b/libedataserver/e-system-source.c
new file mode 100644
index 0000000..12a7d1a
--- /dev/null
+++ b/libedataserver/e-system-source.c
@@ -0,0 +1,117 @@
+/*
+ * e-system-source.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-system-source.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-data-server-util.h"
+
+G_DEFINE_TYPE (
+	ESystemSource,
+	e_system_source,
+	E_TYPE_SOURCE)
+
+static void
+system_source_notify (GObject *object,
+                      GParamSpec *pspec)
+{
+	ESource *source = E_SOURCE (object);
+
+	/* GObject does not allow subclasses to override property flags,
+	 * so we'll keep the "backend-name" and "display-name" properties
+	 * fixed by intercepting attempts to change them and setting them
+	 * back to their proper values.  Hokey but works. */
+
+	if (g_strcmp0 (pspec->name, "backend-name") == 0) {
+		if (e_source_get_backend_name (source) != NULL) {
+			e_source_set_backend_name (source, NULL);
+			return;
+		}
+	}
+
+	if (g_strcmp0 (pspec->name, "display-name") == 0) {
+		const gchar *display_name;
+		const gchar *proper_value;
+
+		display_name = e_source_get_display_name (source);
+		proper_value = _("Personal");
+
+		if (g_strcmp0 (display_name, proper_value) != 0) {
+			e_source_set_display_name (source, proper_value);
+			return;
+		}
+	}
+
+	/* Chain up to parent's notify() method. */
+	G_OBJECT_CLASS (e_system_source_parent_class)->notify (object, pspec);
+}
+
+static void
+e_system_source_class_init (ESystemSourceClass *class)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->notify = system_source_notify;
+}
+
+static void
+e_system_source_init (ESystemSource *source)
+{
+}
+
+ESource *
+e_system_source_new (void)
+{
+	GSettings *settings;
+	ESource *source;
+	GFile *file;
+	const gchar *data_dir;
+	gchar *path;
+
+	data_dir = e_get_user_data_dir ();
+	path = g_build_filename (data_dir, "sources", "system", NULL);
+	file = g_file_new_for_path (path);
+	g_free (path);
+
+	/* This function must not fail, so if a "system" key file
+	 * exists and fails to load, delete it and try again. */
+	source = g_initable_new (
+		E_TYPE_SYSTEM_SOURCE, NULL, NULL, "file", file, NULL);
+	if (source == NULL) {
+		g_file_delete (file, NULL, NULL);
+		source = g_initable_new (
+			E_TYPE_SYSTEM_SOURCE, NULL, NULL, "file", file, NULL);
+	}
+
+	g_object_unref (file);
+
+	g_return_val_if_fail (E_IS_SYSTEM_SOURCE (source), NULL);
+
+	/* Set the "parent" key directly through its GSettings.
+	 *
+	 * XXX To set this during object construction we would have
+	 *     to override the GInitable interface.  Too much hassle
+	 *     for now.  Maybe revisit this in the future. */
+	settings = e_source_get_settings (source);
+	g_settings_set_string (settings, "parent", "local");
+
+	return source;
+}
diff --git a/libedataserver/e-system-source.h b/libedataserver/e-system-source.h
new file mode 100644
index 0000000..959a8f2
--- /dev/null
+++ b/libedataserver/e-system-source.h
@@ -0,0 +1,63 @@
+/*
+ * e-system-source.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_SYSTEM_SOURCE_H
+#define E_SYSTEM_SOURCE_H
+
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SYSTEM_SOURCE \
+	(e_system_source_get_type ())
+#define E_SYSTEM_SOURCE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SYSTEM_SOURCE, ESystemSource))
+#define E_SYSTEM_SOURCE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SYSTEM_SOURCE, ESystemSourceClass))
+#define E_IS_SYSTEM_SOURCE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SYSTEM_SOURCE))
+#define E_IS_SYSTEM_SOURCE_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SYSTEM_SOURCE))
+#define E_SYSTEM_SOURCE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE, ESystemSourceClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESystemSource ESystemSource;
+typedef struct _ESystemSourceClass ESystemSourceClass;
+typedef struct _ESystemSourcePrivate ESystemSourcePrivate;
+
+struct _ESystemSource {
+	ESource parent;
+	ESystemSourcePrivate *priv;
+};
+
+struct _ESystemSourceClass {
+	ESourceClass parent_class;
+};
+
+GType		e_system_source_get_type	(void) G_GNUC_CONST;
+ESource *	e_system_source_new		(void);
+
+G_END_DECLS
+
+#endif /* E_SYSTEM_SOURCE_H */
diff --git a/libedataserver/libedataserver.pc.in b/libedataserver/libedataserver.pc.in
index 460a9cb..4cd47bc 100644
--- a/libedataserver/libedataserver.pc.in
+++ b/libedataserver/libedataserver.pc.in
@@ -10,6 +10,6 @@ privincludedir= privincludedir@
 Name: libedataserver
 Description: Utility library for Evolution Data Server
 Version: @VERSION@
-Requires: gio-2.0 gmodule-2.0 libxml-2.0 gconf-2.0 libsoup-2.4
+Requires: gio-2.0 gmodule-2.0 camel-1.2 gnome-keyring-1 libxml-2.0 gconf-2.0 libsoup-2.4
 Libs: -L${libdir} -ledataserver- API_VERSION@
 Cflags: -I${privincludedir}



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