[evolution-data-server/account-mgmt: 9/42] Add new ESource classes.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/account-mgmt: 9/42] Add new ESource classes.
- Date: Sun, 20 May 2012 19:46:32 +0000 (UTC)
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]