[evolution-data-server] evo-I#1639 - Filter messages by condition if sender is in address book
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] evo-I#1639 - Filter messages by condition if sender is in address book
- Date: Fri, 24 Sep 2021 12:11:38 +0000 (UTC)
commit e248367d50b9f1b7d92cb5154cfd9844cba9f5ef
Author: Milan Crha <mcrha redhat com>
Date: Fri Sep 24 14:10:25 2021 +0200
evo-I#1639 - Filter messages by condition if sender is in address book
Related to https://gitlab.gnome.org/GNOME/evolution/-/issues/1639
docs/reference/camel/camel-docs.sgml.in | 4 +
.../evolution-data-server-docs.sgml.in | 4 +
.../backends/file/e-book-backend-file.c | 57 +++++
.../backends/ldap/e-book-backend-ldap.c | 242 +++++++++++++++++++++
.../libebook-contacts/e-book-contacts-utils.c | 41 ++++
.../libebook-contacts/e-book-contacts-utils.h | 4 +
src/addressbook/libebook/e-book-client.c | 159 ++++++++++++++
src/addressbook/libebook/e-book-client.h | 14 ++
.../libedata-book/e-book-backend-sync.c | 57 +++++
.../libedata-book/e-book-backend-sync.h | 12 +-
src/addressbook/libedata-book/e-book-backend.c | 182 ++++++++++++++++
src/addressbook/libedata-book/e-book-backend.h | 23 +-
src/addressbook/libedata-book/e-book-cache.c | 88 ++++++++
src/addressbook/libedata-book/e-book-cache.h | 4 +
.../libedata-book/e-book-meta-backend.c | 24 ++
src/addressbook/libedata-book/e-data-book.c | 97 +++++++++
src/addressbook/libedata-book/e-data-book.h | 5 +
src/camel/camel-filter-search.c | 62 +++++-
src/camel/camel-folder-search.c | 68 ++++++
src/camel/camel-folder-search.h | 11 +-
src/camel/camel-session.c | 62 ++++++
src/camel/camel-session.h | 17 +-
.../org.gnome.evolution.dataserver.AddressBook.xml | 5 +
tests/libedata-book/test-book-meta-backend.c | 43 ++++
24 files changed, 1280 insertions(+), 5 deletions(-)
---
diff --git a/docs/reference/camel/camel-docs.sgml.in b/docs/reference/camel/camel-docs.sgml.in
index 4f54beb54..4eb653763 100644
--- a/docs/reference/camel/camel-docs.sgml.in
+++ b/docs/reference/camel/camel-docs.sgml.in
@@ -298,6 +298,10 @@
<title>Index of deprecated symbols</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
+ <index id="api-index-3-44" role="3.44">
+ <title>Index of new symbols in 3.44</title>
+ <xi:include href="xml/api-index-3.44.xml"><xi:fallback /></xi:include>
+ </index>
<index id="api-index-3-42" role="3.42">
<title>Index of new symbols in 3.42</title>
<xi:include href="xml/api-index-3.42.xml"><xi:fallback /></xi:include>
diff --git a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
index 874fc9384..c5ce8efa6 100644
--- a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
+++ b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
@@ -361,6 +361,10 @@
<title>Index of deprecated symbols</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
+ <index id="api-index-3-44" role="3.44">
+ <title>Index of new symbols in 3.44</title>
+ <xi:include href="xml/api-index-3.44.xml"><xi:fallback /></xi:include>
+ </index>
<index id="api-index-3-42" role="3.42">
<title>Index of new symbols in 3.42</title>
<xi:include href="xml/api-index-3.42.xml"><xi:fallback /></xi:include>
diff --git a/src/addressbook/backends/file/e-book-backend-file.c
b/src/addressbook/backends/file/e-book-backend-file.c
index 887c56b05..fd6b571e8 100644
--- a/src/addressbook/backends/file/e-book-backend-file.c
+++ b/src/addressbook/backends/file/e-book-backend-file.c
@@ -1737,6 +1737,62 @@ book_backend_file_get_contact_list_uids_sync (EBookBackendSync *backend,
return success;
}
+static gboolean
+book_backend_file_gather_addresses_cb (gpointer ptr_name,
+ gpointer ptr_email,
+ gpointer user_data)
+{
+ GPtrArray *array = user_data;
+ const gchar *email = ptr_email;
+
+ if (email && *email)
+ g_ptr_array_add (array, e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_IS, email));
+
+ return TRUE;
+}
+
+static gboolean
+book_backend_file_contains_email_sync (EBookBackendSync *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBookQuery *book_query = NULL;
+ GPtrArray *array;
+ gchar *sexp = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (email_address != NULL, FALSE);
+
+ d (printf ("book_backend_file_contains_email_sync (%s)\n", email_address));
+
+ array = g_ptr_array_new_full (1, (GDestroyNotify) e_book_query_unref);
+
+ e_book_util_foreach_address (email_address, book_backend_file_gather_addresses_cb, array);
+
+ if (array->len > 0)
+ book_query = e_book_query_or (array->len, (EBookQuery **) array->pdata, FALSE);
+
+ if (book_query)
+ sexp = e_book_query_to_string (book_query);
+
+ if (sexp) {
+ GSList *uids = NULL;
+
+ success = book_backend_file_get_contact_list_uids_sync (backend, sexp,
+ &uids, cancellable, error);
+ success = success && uids != NULL;
+
+ g_slist_free_full (uids, g_free);
+ }
+
+ g_clear_pointer (&book_query, e_book_query_unref);
+ g_ptr_array_unref (array);
+ g_free (sexp);
+
+ return success;
+}
+
static void
book_backend_file_start_view (EBookBackend *backend,
EDataBookView *book_view)
@@ -2141,6 +2197,7 @@ e_book_backend_file_class_init (EBookBackendFileClass *class)
backend_sync_class->get_contact_sync = book_backend_file_get_contact_sync;
backend_sync_class->get_contact_list_sync = book_backend_file_get_contact_list_sync;
backend_sync_class->get_contact_list_uids_sync = book_backend_file_get_contact_list_uids_sync;
+ backend_sync_class->contains_email_sync = book_backend_file_contains_email_sync;
backend_class = E_BOOK_BACKEND_CLASS (class);
backend_class->impl_get_backend_property = book_backend_file_get_backend_property;
diff --git a/src/addressbook/backends/ldap/e-book-backend-ldap.c
b/src/addressbook/backends/ldap/e-book-backend-ldap.c
index 74c1b8961..0ceaf8b71 100644
--- a/src/addressbook/backends/ldap/e-book-backend-ldap.c
+++ b/src/addressbook/backends/ldap/e-book-backend-ldap.c
@@ -5756,6 +5756,247 @@ book_backend_ldap_get_contact_list_uids (EBookBackend *backend,
}
}
+typedef struct {
+ LDAPOp op;
+ gboolean found;
+} LDAPContainsEmailOp;
+
+static void
+contains_email_dtor (LDAPOp *op)
+{
+ LDAPContainsEmailOp *contains_email_op = (LDAPContainsEmailOp *) op;
+
+ g_slice_free (LDAPContainsEmailOp, contains_email_op);
+}
+
+static void
+contains_email_handler (LDAPOp *op,
+ LDAPMessage *res)
+{
+ LDAPContainsEmailOp *contains_email_op = (LDAPContainsEmailOp *) op;
+ EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
+ LDAPMessage *e;
+ gint msg_type;
+
+ if (enable_debug)
+ printf ("%s:\n", G_STRFUNC);
+
+ g_rec_mutex_lock (&eds_ldap_handler_lock);
+ if (!bl->priv->ldap) {
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+ e_data_book_respond_contains_email (op->book, op->opid, EC_ERROR_NOT_CONNECTED (), FALSE);
+ ldap_op_finished (op);
+ if (enable_debug)
+ printf ("%s: ldap handler is NULL\n", G_STRFUNC);
+ return;
+ }
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+
+ msg_type = ldap_msgtype (res);
+ if (msg_type == LDAP_RES_SEARCH_ENTRY) {
+ g_rec_mutex_lock (&eds_ldap_handler_lock);
+ if (bl->priv->ldap)
+ e = ldap_first_entry (bl->priv->ldap, res);
+ else
+ e = NULL;
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+
+ while (NULL != e) {
+ EContact *contact;
+ gchar *uid = NULL;
+
+ contact = build_contact_from_entry (bl, e, NULL, &uid);
+ g_clear_object (&contact);
+
+ if (enable_debug)
+ printf ("uid = %s\n", uid ? uid : "(null)");
+
+ if (uid) {
+ contains_email_op->found = TRUE;
+ g_free (uid);
+ }
+
+ g_rec_mutex_lock (&eds_ldap_handler_lock);
+ if (bl->priv->ldap)
+ e = ldap_next_entry (bl->priv->ldap, e);
+ else
+ e = NULL;
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+ }
+ } else if (msg_type == LDAP_RES_SEARCH_REFERENCE) {
+ /* ignore references */
+ } else if (msg_type == LDAP_RES_SEARCH_RESULT) {
+ gchar *ldap_error_msg = NULL;
+ gint ldap_error;
+
+ g_rec_mutex_lock (&eds_ldap_handler_lock);
+ if (bl->priv->ldap) {
+ ldap_parse_result (
+ bl->priv->ldap, res, &ldap_error,
+ NULL, &ldap_error_msg, NULL, NULL, 0);
+ } else {
+ ldap_error = LDAP_SERVER_DOWN;
+ }
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+ if (ldap_error != LDAP_SUCCESS) {
+ printf (
+ "%s: %02X (%s), additional info: %s\n", G_STRFUNC,
+ ldap_error,
+ ldap_err2string (ldap_error), ldap_error_msg);
+ }
+ if (ldap_error_msg)
+ ldap_memfree (ldap_error_msg);
+
+ if (ldap_error == LDAP_TIMELIMIT_EXCEEDED)
+ e_data_book_respond_contains_email (
+ op->book, op->opid,
+ EC_ERROR (E_CLIENT_ERROR_SEARCH_TIME_LIMIT_EXCEEDED),
+ FALSE);
+ else if (ldap_error == LDAP_SIZELIMIT_EXCEEDED)
+ e_data_book_respond_contains_email (
+ op->book, op->opid,
+ EC_ERROR (E_CLIENT_ERROR_SEARCH_SIZE_LIMIT_EXCEEDED),
+ FALSE);
+ else if (ldap_error == LDAP_SUCCESS)
+ e_data_book_respond_contains_email (
+ op->book, op->opid,
+ NULL,
+ contains_email_op->found);
+ else
+ e_data_book_respond_contains_email (
+ op->book, op->opid,
+ ldap_error_to_response (ldap_error),
+ contains_email_op->found);
+
+ ldap_op_finished (op);
+ } else {
+ g_warning ("unhandled search result type %d returned", msg_type);
+ e_data_book_respond_contains_email (
+ op->book, op->opid,
+ e_client_error_create_fmt (E_CLIENT_ERROR_OTHER_ERROR,
+ _("%s: Unhandled search result type %d returned"), G_STRFUNC, msg_type),
+ FALSE);
+ ldap_op_finished (op);
+ }
+}
+
+static gboolean
+book_backend_ldap_gather_addresses_cb (gpointer ptr_name,
+ gpointer ptr_email,
+ gpointer user_data)
+{
+ GPtrArray *array = user_data;
+ const gchar *email = ptr_email;
+
+ if (email && *email)
+ g_ptr_array_add (array, e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_IS, email));
+
+ return TRUE;
+}
+
+static void
+book_backend_ldap_contains_email (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const gchar *email_address)
+{
+ EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
+ EBookQuery *book_query = NULL;
+ GPtrArray *array;
+ gchar *sexp = NULL;
+ GError *error = NULL;
+ gboolean found = FALSE, finished = TRUE;
+
+ array = g_ptr_array_new_full (1, (GDestroyNotify) e_book_query_unref);
+
+ e_book_util_foreach_address (email_address, book_backend_ldap_gather_addresses_cb, array);
+
+ if (array->len > 0)
+ book_query = e_book_query_or (array->len, (EBookQuery **) array->pdata, FALSE);
+
+ if (book_query)
+ sexp = e_book_query_to_string (book_query);
+
+ if (sexp) {
+ if (bl->priv->cache) {
+ GList *contacts;
+ contacts = e_book_backend_cache_get_contacts (bl->priv->cache, sexp);
+ found = contacts != NULL;
+ g_list_free_full (contacts, g_object_unref);
+ }
+
+ if (!found && !e_backend_get_online (E_BACKEND (backend))) {
+ if (!bl->priv->marked_for_offline)
+ error = EC_ERROR (E_CLIENT_ERROR_REPOSITORY_OFFLINE);
+ } else if (!found) {
+ LDAPContainsEmailOp *contains_email_op;
+ gint contains_email_msgid = 0;
+ EDataBookView *book_view;
+ gint ldap_error;
+ gchar *ldap_query;
+
+ g_rec_mutex_lock (&eds_ldap_handler_lock);
+ if (!bl->priv->ldap) {
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+ error = EC_ERROR_NOT_CONNECTED ();
+ goto out;
+ }
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+
+ contains_email_op = g_slice_new0 (LDAPContainsEmailOp);
+ book_view = find_book_view (bl);
+
+ ldap_query = e_book_backend_ldap_build_query (bl, sexp);
+
+ if (enable_debug)
+ printf ("checking emails with filter: '%s'\n", ldap_query);
+
+ do {
+ g_rec_mutex_lock (&eds_ldap_handler_lock);
+ if (bl->priv->ldap) {
+ ldap_error = ldap_search_ext (
+ bl->priv->ldap,
+ bl->priv->ldap_rootdn,
+ bl->priv->ldap_scope,
+ ldap_query,
+ NULL, 0, NULL, NULL,
+ NULL, /* XXX timeout */
+ 1 /* sizelimit */, &contains_email_msgid);
+ } else {
+ ldap_error = LDAP_SERVER_DOWN;
+ }
+ g_rec_mutex_unlock (&eds_ldap_handler_lock);
+ } while (e_book_backend_ldap_reconnect (bl, book_view, ldap_error));
+
+ g_free (ldap_query);
+
+ if (ldap_error == LDAP_SUCCESS) {
+ finished = FALSE;
+ ldap_op_add (
+ (LDAPOp *) contains_email_op, backend, book,
+ book_view, opid, contains_email_msgid,
+ contains_email_handler, contains_email_dtor);
+ } else {
+ error = ldap_error_to_response (ldap_error);
+ contains_email_dtor ((LDAPOp *) contains_email_op);
+ }
+ }
+ } else {
+ error = EC_ERROR (E_CLIENT_ERROR_INVALID_QUERY);
+ }
+
+ out:
+ if (finished)
+ e_data_book_respond_contains_email (book, opid, error, found);
+ else
+ g_clear_error (&error);
+
+ g_clear_pointer (&book_query, e_book_query_unref);
+ g_ptr_array_unref (array);
+ g_free (sexp);
+}
+
static ESourceAuthenticationResult
book_backend_ldap_authenticate_sync (EBackend *backend,
const ENamedParameters *credentials,
@@ -6027,6 +6268,7 @@ e_book_backend_ldap_class_init (EBookBackendLDAPClass *class)
book_backend_class->impl_get_contact = book_backend_ldap_get_contact;
book_backend_class->impl_get_contact_list = book_backend_ldap_get_contact_list;
book_backend_class->impl_get_contact_list_uids = book_backend_ldap_get_contact_list_uids;
+ book_backend_class->impl_contains_email = book_backend_ldap_contains_email;
book_backend_class->impl_start_view = book_backend_ldap_start_view;
book_backend_class->impl_stop_view = book_backend_ldap_stop_view;
book_backend_class->impl_refresh = book_backend_ldap_refresh;
diff --git a/src/addressbook/libebook-contacts/e-book-contacts-utils.c
b/src/addressbook/libebook-contacts/e-book-contacts-utils.c
index fc16d544f..d4efc8f67 100644
--- a/src/addressbook/libebook-contacts/e-book-contacts-utils.c
+++ b/src/addressbook/libebook-contacts/e-book-contacts-utils.c
@@ -166,3 +166,44 @@ e_book_util_conflict_resolution_to_operation_flags (EConflictResolution conflict
return E_BOOK_OPERATION_FLAG_CONFLICT_KEEP_LOCAL;
}
+
+/**
+ * e_book_util_foreach_address:
+ * @email_address: one or more email addresses as string
+ * @func: (scope call): a function to call for each email
+ * @user_data (closure func): user data passed to @func
+ *
+ * Parses the @email_address and calls @func for each found address.
+ * The first parameter of the @func is the name, the second parameter
+ * of the @func is the email, the third parameters of the @func is
+ * the @user_data. The @func returns %TRUE, to continue processing.
+ *
+ * Since: 3.44
+ **/
+void
+e_book_util_foreach_address (const gchar *email_address,
+ GHRFunc func,
+ gpointer user_data)
+{
+ CamelInternetAddress *address;
+ const gchar *name, *email;
+ gint index;
+
+ g_return_if_fail (func != NULL);
+
+ if (!email_address || !*email_address)
+ return;
+
+ address = camel_internet_address_new ();
+ if (!camel_address_decode (CAMEL_ADDRESS (address), email_address)) {
+ g_object_unref (address);
+ return;
+ }
+
+ for (index = 0; camel_internet_address_get (address, index, &name, &email); index++) {
+ if (!func ((gpointer) name, (gpointer) email, user_data))
+ break;
+ }
+
+ g_object_unref (address);
+}
diff --git a/src/addressbook/libebook-contacts/e-book-contacts-utils.h
b/src/addressbook/libebook-contacts/e-book-contacts-utils.h
index 62781d490..1bded4f93 100644
--- a/src/addressbook/libebook-contacts/e-book-contacts-utils.h
+++ b/src/addressbook/libebook-contacts/e-book-contacts-utils.h
@@ -114,6 +114,10 @@ EConflictResolution
guint32 e_book_util_conflict_resolution_to_operation_flags /* bit-or of EBookOperationFlags */
(EConflictResolution conflict_resolution);
+void e_book_util_foreach_address (const gchar *email_address,
+ GHRFunc func,
+ gpointer user_data);
+
#ifndef EDS_DISABLE_DEPRECATED
/**
diff --git a/src/addressbook/libebook/e-book-client.c b/src/addressbook/libebook/e-book-client.c
index 7265dd5e2..91776c464 100644
--- a/src/addressbook/libebook/e-book-client.c
+++ b/src/addressbook/libebook/e-book-client.c
@@ -79,6 +79,7 @@ struct _AsyncContext {
gchar *uid;
GMainContext *context;
guint32 opflags;
+ gboolean success;
};
struct _SignalClosure {
@@ -3871,6 +3872,164 @@ e_book_client_get_contacts_uids_sync (EBookClient *client,
return TRUE;
}
+/* Helper for e_book_client_contains_email() */
+static void
+book_client_contains_email_thread (GSimpleAsyncResult *simple,
+ GObject *source_object,
+ GCancellable *cancellable)
+{
+ AsyncContext *async_context;
+ GError *local_error = NULL;
+
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ async_context->success = e_book_client_contains_email_sync (E_BOOK_CLIENT (source_object),
async_context->sexp, cancellable, &local_error);
+
+ if (local_error != NULL)
+ g_simple_async_result_take_error (simple, local_error);
+}
+
+/**
+ * e_book_client_contains_email:
+ * @client: an #EBookClient
+ * @email_address: an email address
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Asynchronously checks whether contains an @email_address. When the @email_address
+ * contains multiple addresses, then returns %TRUE when at least one
+ * address exists in the address book.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_book_client_contains_email_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.44
+ **/
+void
+e_book_client_contains_email (EBookClient *client,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+
+ g_return_if_fail (E_IS_BOOK_CLIENT (client));
+ g_return_if_fail (email_address != NULL);
+
+ async_context = g_slice_new0 (AsyncContext);
+ async_context->sexp = g_strdup (email_address);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (client), callback, user_data,
+ e_book_client_contains_email);
+
+ 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, book_client_contains_email_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (simple);
+}
+
+/**
+ * e_book_client_contains_email_finish:
+ * @client: an #EBookClient
+ * @result: a #GAsyncResult
+ * @error: a #GError to set an error, if any
+ *
+ * Finishes previous call of e_book_client_contains_email().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_book_client_contains_email_finish (EBookClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (client),
+ e_book_client_contains_email), 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;
+
+ return async_context->success;
+}
+
+/**
+ * e_book_client_cotnains_email_sync:
+ * @client: an #EBookClient
+ * @email_address: an email address
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: a #GError to set an error, if any
+ *
+ * Checks whether contains an @email_address. When the @email_address
+ * contains multiple addresses, then returns %TRUE when at least one
+ * address exists in the address book.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE when found the @email_address, %FALSE on failure
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_book_client_contains_email_sync (EBookClient *client,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GError *local_error = NULL;
+ gchar *utf8_email_address;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
+ g_return_val_if_fail (email_address != NULL, FALSE);
+
+ if (client->priv->direct_backend != NULL) {
+ /* Direct backend is not using D-Bus (obviously),
+ * so no need to strip D-Bus info from the error. */
+ success = e_book_backend_contains_email_sync (
+ client->priv->direct_backend,
+ email_address, cancellable, error);
+
+ return success;
+ }
+
+ utf8_email_address = e_util_utf8_make_valid (email_address);
+
+ e_dbus_address_book_call_contains_email_sync (
+ client->priv->dbus_proxy, utf8_email_address, &success,
+ cancellable, &local_error);
+
+ g_free (utf8_email_address);
+
+ if (local_error != NULL) {
+ g_dbus_error_strip_remote_error (local_error);
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ return success;
+}
+
/* Helper for e_book_client_get_view() */
static void
book_client_get_view_in_dbus_thread (GSimpleAsyncResult *simple,
diff --git a/src/addressbook/libebook/e-book-client.h b/src/addressbook/libebook/e-book-client.h
index eca77f109..574b3cfa0 100644
--- a/src/addressbook/libebook/e-book-client.h
+++ b/src/addressbook/libebook/e-book-client.h
@@ -277,6 +277,20 @@ gboolean e_book_client_get_contacts_uids_sync
GSList **out_contact_uids,
GCancellable *cancellable,
GError **error);
+void e_book_client_contains_email (EBookClient *client,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_book_client_contains_email_finish
+ (EBookClient *client,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_book_client_contains_email_sync
+ (EBookClient *client,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error);
void e_book_client_get_view (EBookClient *client,
const gchar *sexp,
GCancellable *cancellable,
diff --git a/src/addressbook/libedata-book/e-book-backend-sync.c
b/src/addressbook/libedata-book/e-book-backend-sync.c
index 852b87dab..405218ddf 100644
--- a/src/addressbook/libedata-book/e-book-backend-sync.c
+++ b/src/addressbook/libedata-book/e-book-backend-sync.c
@@ -233,6 +233,24 @@ book_backend_sync_get_contact_list_uids_sync (EBookBackendSync *backend,
return success;
}
+static void
+book_backend_sync_contains_email (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const gchar *email_address)
+{
+ GError *error = NULL;
+ gboolean found;
+
+ g_return_if_fail (E_IS_BOOK_BACKEND_SYNC (backend));
+ g_return_if_fail (E_IS_DATA_BOOK (book));
+
+ found = e_book_backend_sync_contains_email (E_BOOK_BACKEND_SYNC (backend), email_address,
cancellable, &error);
+
+ e_data_book_respond_contains_email (book, opid, error, found);
+}
+
static void
e_book_backend_sync_class_init (EBookBackendSyncClass *klass)
{
@@ -249,6 +267,7 @@ e_book_backend_sync_class_init (EBookBackendSyncClass *klass)
book_backend_class->impl_get_contact = book_backend_sync_get_contact;
book_backend_class->impl_get_contact_list = book_backend_sync_get_contact_list;
book_backend_class->impl_get_contact_list_uids = book_backend_sync_get_contact_list_uids;
+ book_backend_class->impl_contains_email = book_backend_sync_contains_email;
}
static void
@@ -581,3 +600,41 @@ e_book_backend_sync_get_contact_list_uids (EBookBackendSync *backend,
return FALSE;
}
+
+/**
+ * e_book_backend_sync_contains_email:
+ * @backend: an #EBookBackendSync
+ * @email_address: an email address
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Checks whether contains an @email_address. When the @email_address
+ * contains multiple addresses, then returns %TRUE when at least one
+ * address exists in the address book.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE when found the @email_address, %FALSE on failure
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_book_backend_sync_contains_email (EBookBackendSync *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBookBackendSyncClass *klass;
+
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), FALSE);
+
+ klass = E_BOOK_BACKEND_SYNC_GET_CLASS (backend);
+ g_return_val_if_fail (klass != NULL, FALSE);
+
+ if (klass->contains_email_sync)
+ return klass->contains_email_sync (backend, email_address, cancellable, error);
+
+ g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
+
+ return FALSE;
+}
diff --git a/src/addressbook/libedata-book/e-book-backend-sync.h
b/src/addressbook/libedata-book/e-book-backend-sync.h
index 51176c8be..2a8ef14c9 100644
--- a/src/addressbook/libedata-book/e-book-backend-sync.h
+++ b/src/addressbook/libedata-book/e-book-backend-sync.h
@@ -139,8 +139,13 @@ struct _EBookBackendSyncClass {
GCancellable *cancellable,
GError **error);
+ gboolean (*contains_email_sync) (EBookBackendSync *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error);
+
/* Padding for future expansion */
- gpointer reserved_padding[20];
+ gpointer reserved_padding[19];
};
GType e_book_backend_sync_get_type (void) G_GNUC_CONST;
@@ -188,6 +193,11 @@ gboolean e_book_backend_sync_get_contact_list_uids
GSList **out_uids, /* gchar * */
GCancellable *cancellable,
GError **error);
+gboolean e_book_backend_sync_contains_email
+ (EBookBackendSync *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/src/addressbook/libedata-book/e-book-backend.c b/src/addressbook/libedata-book/e-book-backend.c
index f444bf3a5..852d6e4c2 100644
--- a/src/addressbook/libedata-book/e-book-backend.c
+++ b/src/addressbook/libedata-book/e-book-backend.c
@@ -2569,6 +2569,188 @@ e_book_backend_get_contact_list_uids_finish (EBookBackend *backend,
return TRUE;
}
+/**
+ * e_book_backend_contains_email_sync:
+ * @backend: an #EBookBackend
+ * @email_address: an email address
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Checks whether contains an @email_address. When the @email_address
+ * contains multiple addresses, then returns %TRUE when at least one
+ * address exists in the address book.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE when found the @email_address, %FALSE on failure
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_book_backend_contains_email_sync (EBookBackend *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EAsyncClosure *closure;
+ GAsyncResult *result;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
+ g_return_val_if_fail (email_address != NULL, FALSE);
+
+ closure = e_async_closure_new ();
+
+ e_book_backend_contains_email (backend, email_address, cancellable,
+ e_async_closure_callback, closure);
+
+ result = e_async_closure_wait (closure);
+
+ success = e_book_backend_contains_email_finish (backend, result, error);
+
+ e_async_closure_free (closure);
+
+ return success;
+}
+
+/* Helper for e_book_backend_contains_email() */
+static void
+book_backend_contains_email_thread (GSimpleAsyncResult *simple,
+ GObject *source_object,
+ GCancellable *cancellable)
+{
+ EBookBackend *backend;
+ EBookBackendClass *klass;
+ EDataBook *data_book;
+ AsyncContext *async_context;
+
+ backend = E_BOOK_BACKEND (source_object);
+
+ klass = E_BOOK_BACKEND_GET_CLASS (backend);
+ g_return_if_fail (klass != NULL);
+
+ if (!klass->impl_contains_email) {
+ g_simple_async_result_take_error (simple, e_client_error_create
(E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
+ g_simple_async_result_complete_in_idle (simple);
+ return;
+ }
+
+ data_book = e_book_backend_ref_data_book (backend);
+ g_return_if_fail (data_book != NULL);
+
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (!e_book_backend_is_opened (backend)) {
+ g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED,
NULL));
+ g_simple_async_result_complete_in_idle (simple);
+ } else {
+ guint32 opid;
+
+ opid = book_backend_stash_operation (backend, simple);
+
+ klass->impl_contains_email (backend, data_book, opid, cancellable, async_context->query);
+ }
+
+ g_object_unref (data_book);
+}
+
+/**
+ * e_book_backend_contains_email:
+ * @backend: an #EBookBackend
+ * @email_address: an email address
+ * @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 checks whether contains an @email_address. When the @email_address
+ * contains multiple addresses, then returns %TRUE when at least one
+ * address exists in the address book.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_book_backend_contains_email_finish() to get the result of the
+ * operation.
+ *
+ * Since: 3.44
+ **/
+void
+e_book_backend_contains_email (EBookBackend *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EBookBackendClass *klass;
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+
+ g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+ g_return_if_fail (email_address != NULL);
+
+ klass = E_BOOK_BACKEND_GET_CLASS (backend);
+ g_return_if_fail (klass != NULL);
+
+ async_context = g_slice_new0 (AsyncContext);
+ async_context->query = g_strdup (email_address);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (backend), callback, user_data,
+ e_book_backend_contains_email);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, async_context, (GDestroyNotify) async_context_free);
+
+ if (klass->impl_contains_email != NULL) {
+ book_backend_push_operation (
+ backend, simple, cancellable, FALSE,
+ book_backend_contains_email_thread);
+ book_backend_dispatch_next_operation (backend);
+
+ } else {
+ g_simple_async_result_take_error (simple, e_client_error_create
(E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
+ g_simple_async_result_complete_in_idle (simple);
+ }
+
+ g_object_unref (simple);
+}
+
+/**
+ * e_book_backend_contains_email_finish:
+ * @backend: an #EBookBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_book_backend_contains_email().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_book_backend_contains_email_finish (EBookBackend *backend,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (backend),
+ e_book_backend_contains_email), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ book_backend_unblock_operations (backend, simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
/**
* e_book_backend_start_view:
* @backend: an #EBookBackend
diff --git a/src/addressbook/libedata-book/e-book-backend.h b/src/addressbook/libedata-book/e-book-backend.h
index aa917098d..fef26fd2d 100644
--- a/src/addressbook/libedata-book/e-book-backend.h
+++ b/src/addressbook/libedata-book/e-book-backend.h
@@ -97,6 +97,7 @@ struct _EBookBackend {
* @impl_dup_locale: Return the currently set locale setting (must be a string duplicate, for thread safety).
* @impl_create_cursor: Create an #EDataBookCursor
* @impl_delete_cursor: Delete an #EDataBookCursor previously created by this backend
+ * @impl_contains_email: Checkes whether the backend contains an email address
* @closed: A signal notifying that the backend was closed
* @shutdown: A signal notifying that the backend is being shut down
*
@@ -198,8 +199,14 @@ struct _EBookBackendClass {
const gchar *sender);
void (*shutdown) (EBookBackend *backend);
+ void (*impl_contains_email) (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const gchar *email_address);
+
/* Padding for future expansion */
- gpointer reserved_padding[20];
+ gpointer reserved_padding[19];
};
GType e_book_backend_get_type (void) G_GNUC_CONST;
@@ -342,6 +349,20 @@ gboolean e_book_backend_get_contact_list_uids_finish
GAsyncResult *result,
GQueue *out_uids,
GError **error);
+gboolean e_book_backend_contains_email_sync
+ (EBookBackend *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error);
+void e_book_backend_contains_email (EBookBackend *backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_book_backend_contains_email_finish
+ (EBookBackend *backend,
+ GAsyncResult *result,
+ GError **error);
void e_book_backend_start_view (EBookBackend *backend,
EDataBookView *view);
diff --git a/src/addressbook/libedata-book/e-book-cache.c b/src/addressbook/libedata-book/e-book-cache.c
index abe06d20f..1e62e7d57 100644
--- a/src/addressbook/libedata-book/e-book-cache.c
+++ b/src/addressbook/libedata-book/e-book-cache.c
@@ -5541,6 +5541,94 @@ e_book_cache_search_with_callback (EBookCache *book_cache,
return ebc_search_internal (book_cache, sexp, SEARCH_FULL, NULL, func, user_data, cancellable, error);
}
+typedef struct _ContainsEmailData {
+ GString *query;
+ const gchar *dbname;
+} ContainsEmailData;
+
+static gboolean
+ebc_gather_addresses_cb (gpointer in_name,
+ gpointer in_email,
+ gpointer user_data)
+{
+ ContainsEmailData *ced = user_data;
+ const gchar *email = in_email;
+
+ if (email && *email) {
+ if (ced->query->len)
+ g_string_append (ced->query, " OR ");
+ else
+ e_cache_sqlite_stmt_append_printf (ced->query, "SELECT COUNT(*) FROM %Q WHERE ",
ced->dbname);
+
+ e_cache_sqlite_stmt_append_printf (ced->query, "%Q.value LIKE %Q", ced->dbname, email);
+ }
+
+ return TRUE;
+}
+
+/**
+ * e_book_cache_contains_email:
+ * @book_cache: an #EBookCache
+ * @email_address: an email address to check for
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Checks whether contains an @email_address. When the @email_address
+ * contains multiple addresses, then returns %TRUE when at least one
+ * address exists in the cache.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE when found the @email_address, %FALSE on failure
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_book_cache_contains_email (EBookCache *book_cache,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ContainsEmailData ced;
+ gboolean success = FALSE;
+ gint ii;
+
+ g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE);
+ g_return_val_if_fail (email_address != NULL, FALSE);
+
+ for (ii = 0; ii < book_cache->priv->n_summary_fields; ii++) {
+ if (book_cache->priv->summary_fields[ii].field_id == E_CONTACT_EMAIL)
+ break;
+ }
+
+ if (ii == book_cache->priv->n_summary_fields) {
+ g_set_error_literal (error, E_CACHE_ERROR, E_CACHE_ERROR_UNSUPPORTED_FIELD,
+ _("Search by email not supported"));
+ return FALSE;
+ }
+
+ ced.query = g_string_new ("");
+ ced.dbname = book_cache->priv->summary_fields[ii].aux_table;
+
+ e_book_util_foreach_address (email_address, ebc_gather_addresses_cb, &ced);
+
+ if (ced.query->len == 0) {
+ g_set_error_literal (error, E_CACHE_ERROR, E_CACHE_ERROR_CONSTRAINT,
+ _("No email address provided"));
+ } else {
+ gint n_found = 0;
+
+ g_string_append (ced.query, " LIMIT 1");
+
+ success = e_cache_sqlite_select (E_CACHE (book_cache), ced.query->str, ebc_get_int_cb,
&n_found, cancellable, error);
+ success = success && n_found > 0;
+ }
+
+ g_string_free (ced.query, TRUE);
+
+ return success;
+}
+
/**
* e_book_cache_cursor_new:
* @book_cache: An #EBookCache
diff --git a/src/addressbook/libedata-book/e-book-cache.h b/src/addressbook/libedata-book/e-book-cache.h
index 170dc6b9c..3a0d6b180 100644
--- a/src/addressbook/libedata-book/e-book-cache.h
+++ b/src/addressbook/libedata-book/e-book-cache.h
@@ -304,6 +304,10 @@ gboolean e_book_cache_search_with_callback
gpointer user_data,
GCancellable *cancellable,
GError **error);
+gboolean e_book_cache_contains_email (EBookCache *book_cache,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error);
/* Cursor API */
GType e_book_cache_cursor_get_type (void) G_GNUC_CONST;
diff --git a/src/addressbook/libedata-book/e-book-meta-backend.c
b/src/addressbook/libedata-book/e-book-meta-backend.c
index f2382726f..a8029ab41 100644
--- a/src/addressbook/libedata-book/e-book-meta-backend.c
+++ b/src/addressbook/libedata-book/e-book-meta-backend.c
@@ -1819,6 +1819,29 @@ ebmb_get_contact_list_uids_sync (EBookBackendSync *book_backend,
return e_book_meta_backend_search_uids_sync (E_BOOK_META_BACKEND (book_backend), query, out_uids,
cancellable, error);
}
+static gboolean
+ebmb_contains_email_sync (EBookBackendSync *book_backend,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBookCache *cache;
+ gboolean found;
+
+ g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
+ g_return_val_if_fail (email_address != NULL, FALSE);
+
+ cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (book_backend));
+ if (!cache)
+ return FALSE;
+
+ found = e_book_cache_contains_email (cache, email_address, cancellable, error);
+
+ g_object_unref (cache);
+
+ return found;
+}
+
static void
ebmb_start_view (EBookBackend *book_backend,
EDataBookView *view)
@@ -2513,6 +2536,7 @@ e_book_meta_backend_class_init (EBookMetaBackendClass *klass)
book_backend_sync_class->get_contact_sync = ebmb_get_contact_sync;
book_backend_sync_class->get_contact_list_sync = ebmb_get_contact_list_sync;
book_backend_sync_class->get_contact_list_uids_sync = ebmb_get_contact_list_uids_sync;
+ book_backend_sync_class->contains_email_sync = ebmb_contains_email_sync;
book_backend_class = E_BOOK_BACKEND_CLASS (klass);
book_backend_class->impl_get_backend_property = ebmb_get_backend_property;
diff --git a/src/addressbook/libedata-book/e-data-book.c b/src/addressbook/libedata-book/e-data-book.c
index 95646ae3f..2d48488b2 100644
--- a/src/addressbook/libedata-book/e-data-book.c
+++ b/src/addressbook/libedata-book/e-data-book.c
@@ -1068,6 +1068,56 @@ data_book_handle_get_cursor_cb (EDBusAddressBook *dbus_interface,
return TRUE;
}
+static void
+data_book_complete_contains_email_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ AsyncContext *async_context = user_data;
+ GError *error = NULL;
+ gboolean res;
+
+ res = e_book_backend_contains_email_finish (E_BOOK_BACKEND (source_object), result, &error);
+
+ if (error == NULL) {
+ e_dbus_address_book_complete_contains_email (
+ async_context->dbus_interface,
+ async_context->invocation,
+ res);
+ } else {
+ data_book_convert_to_client_error (error);
+ g_dbus_method_invocation_take_error (
+ async_context->invocation, error);
+ }
+
+ async_context_free (async_context);
+}
+
+static gboolean
+data_book_handle_contains_email_cb (EDBusAddressBook *dbus_interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *in_email,
+ EDataBook *data_book)
+{
+ EBookBackend *backend;
+ AsyncContext *async_context;
+
+ backend = e_data_book_ref_backend (data_book);
+ g_return_val_if_fail (backend != NULL, FALSE);
+
+ async_context = async_context_new (data_book, invocation);
+
+ e_book_backend_contains_email (
+ backend, in_email,
+ async_context->cancellable,
+ data_book_complete_contains_email_cb,
+ async_context);
+
+ g_object_unref (backend);
+
+ return TRUE;
+}
+
static void
data_book_source_unset_last_credentials_required_arguments_cb (GObject *source_object,
GAsyncResult *result,
@@ -1487,6 +1537,49 @@ e_data_book_respond_remove_contacts (EDataBook *book,
g_object_unref (backend);
}
+/**
+ * e_data_book_respond_contains_email:
+ * @book: An #EDataBook
+ * @opid: An operation ID
+ * @error: Operation error, if any, automatically freed if passed it
+ * @found: %TRUE, when found the email in the address book
+ *
+ * Finishes a call to check whether contains an email address.
+ *
+ * Since: 3.44
+ **/
+void
+e_data_book_respond_contains_email (EDataBook *book,
+ guint32 opid,
+ GError *error,
+ gboolean found)
+{
+ EBookBackend *backend;
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (E_IS_DATA_BOOK (book));
+
+ backend = e_data_book_ref_backend (book);
+ g_return_if_fail (backend != NULL);
+
+ simple = e_book_backend_prepare_for_completion (backend, opid, NULL);
+ g_return_if_fail (simple != NULL);
+
+ /* Translators: This is prefix to a detailed error message */
+ g_prefix_error (&error, "%s", _("Cannot find email address: "));
+
+ if (error == NULL) {
+ g_simple_async_result_set_op_res_gboolean (simple, found);
+ } else {
+ g_simple_async_result_take_error (simple, error);
+ }
+
+ g_simple_async_result_complete_in_idle (simple);
+
+ g_object_unref (simple);
+ g_object_unref (backend);
+}
+
/**
* e_data_book_report_error:
* @book: An #EDataBook
@@ -1948,6 +2041,10 @@ e_data_book_init (EDataBook *data_book)
dbus_interface, "handle-get-cursor",
G_CALLBACK (data_book_handle_get_cursor_cb),
data_book);
+ g_signal_connect (
+ dbus_interface, "handle-contains-email",
+ G_CALLBACK (data_book_handle_contains_email_cb),
+ data_book);
g_signal_connect (
dbus_interface, "handle-close",
G_CALLBACK (data_book_handle_close_cb),
diff --git a/src/addressbook/libedata-book/e-data-book.h b/src/addressbook/libedata-book/e-data-book.h
index 18808c15d..962c3758d 100644
--- a/src/addressbook/libedata-book/e-data-book.h
+++ b/src/addressbook/libedata-book/e-data-book.h
@@ -112,6 +112,11 @@ void e_data_book_respond_get_contact_list_uids
guint32 opid,
GError *error,
const GSList *uids); /* gchar * */
+void e_data_book_respond_contains_email
+ (EDataBook *book,
+ guint32 opid,
+ GError *error,
+ gboolean found);
void e_data_book_report_error (EDataBook *book,
const gchar *message);
diff --git a/src/camel/camel-filter-search.c b/src/camel/camel-filter-search.c
index c9854bebc..df429b4cd 100644
--- a/src/camel/camel-filter-search.c
+++ b/src/camel/camel-filter-search.c
@@ -93,6 +93,7 @@ static CamelSExpResult *junk_test (struct _CamelSExp *f, gint argc, struct _Came
static CamelSExpResult *message_location (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv,
FilterMessageSearch *fms);
static CamelSExpResult *make_time_func (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv,
FilterMessageSearch *fms);
static CamelSExpResult *compare_date_func (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv,
FilterMessageSearch *fms);
+static CamelSExpResult *addressbook_contains_func (struct _CamelSExp *f, gint argc, struct _CamelSExpResult
**argv, FilterMessageSearch *fms);
/* builtin functions */
static struct {
@@ -126,7 +127,8 @@ static struct {
{ "junk-test", (CamelSExpFunc) junk_test, 0 },
{ "message-location", (CamelSExpFunc) message_location, 0 },
{ "make-time", (CamelSExpFunc) make_time_func, 0 },
- { "compare-date", (CamelSExpFunc) compare_date_func, 0 }
+ { "compare-date", (CamelSExpFunc) compare_date_func, 0 },
+ { "addressbook-contains",(CamelSExpFunc) addressbook_contains_func, 0 }
};
static void
@@ -1316,6 +1318,64 @@ compare_date_func (CamelSExp *sexp,
return res;
}
+static CamelSExpResult *
+addressbook_contains_func (CamelSExp *sexp,
+ gint argc,
+ CamelSExpResult **argv,
+ FilterMessageSearch *fms)
+{
+ CamelSExpResult *res;
+
+ res = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
+ res->value.boolean = FALSE;
+
+ if (argc == 2) {
+ GError *local_error = NULL;
+ const gchar *book_uid, *header, *email_address = NULL;
+ gboolean address_set = FALSE;
+
+ if (argv[0]->type != CAMEL_SEXP_RES_STRING) {
+ camel_filter_search_log (fms, "addressbook-contains result:%d (incorrect first
argument type, expects string)", res->value.boolean);
+ return res;
+ }
+
+ if (argv[1]->type != CAMEL_SEXP_RES_STRING) {
+ camel_filter_search_log (fms, "addressbook-contains result:%d (incorrect second
argument type, expects string)", res->value.boolean);
+ return res;
+ }
+
+ book_uid = argv[0]->value.string;
+ header = argv[1]->value.string;
+
+ if (header && g_ascii_strcasecmp (header, "From") == 0) {
+ email_address = camel_message_info_get_from (fms->info);
+ address_set = TRUE;
+ } else if (header && g_ascii_strcasecmp (header, "To") == 0) {
+ email_address = camel_message_info_get_to (fms->info);
+ address_set = TRUE;
+ } else if (header && g_ascii_strcasecmp (header, "Cc") == 0) {
+ email_address = camel_message_info_get_cc (fms->info);
+ address_set = TRUE;
+ }
+
+ res->value.boolean = email_address && camel_session_addressbook_contains_sync (fms->session,
+ book_uid, email_address, fms->cancellable, &local_error);
+
+ if (!address_set)
+ camel_filter_search_log (fms, "addressbook-contains result:%d unsupported header
'%s'", res->value.boolean, header);
+ else if (local_error)
+ camel_filter_search_log (fms, "addressbook-contains result:%d for '%s' in '%s'
error:%s", res->value.boolean, email_address, book_uid, local_error->message);
+ else
+ camel_filter_search_log (fms, "addressbook-contains result:%d for '%s' in '%s' ",
res->value.boolean, email_address, book_uid);
+
+ g_clear_error (&local_error);
+ } else {
+ camel_filter_search_log (fms, "addressbook-contains result:%d expects two arguments, received
%d", res->value.boolean, argc);
+ }
+
+ return res;
+}
+
static const gchar *
camel_search_result_to_string (gint value)
{
diff --git a/src/camel/camel-folder-search.c b/src/camel/camel-folder-search.c
index b5db023b2..f865c690c 100644
--- a/src/camel/camel-folder-search.c
+++ b/src/camel/camel-folder-search.c
@@ -229,6 +229,10 @@ static struct {
{ "compare-date",
G_STRUCT_OFFSET (CamelFolderSearchClass, compare_date),
CAMEL_FOLDER_SEARCH_ALWAYS_ENTER },
+
+ { "addressbook-contains",
+ G_STRUCT_OFFSET (CamelFolderSearchClass, addressbook_contains),
+ CAMEL_FOLDER_SEARCH_ALWAYS_ENTER },
};
G_DEFINE_TYPE_WITH_PRIVATE (CamelFolderSearch, camel_folder_search, G_TYPE_OBJECT)
@@ -1799,6 +1803,68 @@ folder_search_compare_date (CamelSExp *sexp,
return res;
}
+static CamelSExpResult *
+folder_search_addressbook_contains (CamelSExp *sexp,
+ gint argc,
+ CamelSExpResult **argv,
+ CamelFolderSearch *search)
+{
+ CamelSExpResult *res;
+
+ r (printf ("executing addressbook-contains\n"));
+
+ if (!search->priv->current) {
+ res = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_ARRAY_PTR);
+ res->value.ptrarray = g_ptr_array_new ();
+
+ return res;
+ }
+
+ res = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
+ res->value.boolean = FALSE;
+
+ if (argc == 2) {
+ const gchar *book_uid, *header, *email_address = NULL;
+ gboolean address_set = FALSE;
+
+ if (argv[0]->type != CAMEL_SEXP_RES_STRING ||
+ argv[1]->type != CAMEL_SEXP_RES_STRING)
+ return res;
+
+ book_uid = argv[0]->value.string;
+ header = argv[1]->value.string;
+
+ if (header && g_ascii_strcasecmp (header, "From") == 0) {
+ email_address = camel_message_info_get_from (search->priv->current);
+ address_set = TRUE;
+ } else if (header && g_ascii_strcasecmp (header, "To") == 0) {
+ email_address = camel_message_info_get_to (search->priv->current);
+ address_set = TRUE;
+ } else if (header && g_ascii_strcasecmp (header, "Cc") == 0) {
+ email_address = camel_message_info_get_cc (search->priv->current);
+ address_set = TRUE;
+ }
+
+ if (address_set) {
+ CamelStore *store;
+ CamelSession *session = NULL;
+
+ store = camel_folder_get_parent_store (search->priv->folder);
+ if (store)
+ session = camel_service_ref_session (CAMEL_SERVICE (store));
+
+ if (session) {
+ res->value.boolean = email_address && camel_session_addressbook_contains_sync
(session,
+ book_uid, email_address, search->priv->cancellable, NULL);
+
+ g_object_unref (session);
+ }
+ }
+ }
+
+ return res;
+}
+
static void
camel_folder_search_class_init (CamelFolderSearchClass *class)
{
@@ -1834,6 +1900,7 @@ camel_folder_search_class_init (CamelFolderSearchClass *class)
class->message_location = folder_search_message_location;
class->make_time = folder_search_make_time;
class->compare_date = folder_search_compare_date;
+ class->addressbook_contains = folder_search_addressbook_contains;
}
static void
@@ -2116,6 +2183,7 @@ do_search_in_memory (CamelFolder *search_in_folder,
"header-contains",
"header-has-words",
"header-ends-with",
+ "addressbook-contains",
NULL };
gint i;
diff --git a/src/camel/camel-folder-search.h b/src/camel/camel-folder-search.h
index be6d6f05f..0b26332f3 100644
--- a/src/camel/camel-folder-search.h
+++ b/src/camel/camel-folder-search.h
@@ -271,8 +271,17 @@ struct _CamelFolderSearchClass {
CamelSExpResult **argv,
CamelFolderSearch *search);
+ /* (addressbook-contains "book_uid" "header-name")
+ * Checks whether at least one address from header "header-name"
+ * is in the address book "book_uid". Not all headers are supported. */
+ CamelSExpResult * (*addressbook_contains)
+ (CamelSExp *sexp,
+ gint argc,
+ CamelSExpResult **argv,
+ CamelFolderSearch *search);
+
/* Padding for future expansion */
- gpointer reserved[18];
+ gpointer reserved[17];
};
GType camel_folder_search_get_type (void) G_GNUC_CONST;
diff --git a/src/camel/camel-session.c b/src/camel/camel-session.c
index 1a060006b..688dc2a71 100644
--- a/src/camel/camel-session.c
+++ b/src/camel/camel-session.c
@@ -1329,6 +1329,68 @@ camel_session_lookup_addressbook (CamelSession *session,
return class->lookup_addressbook (session, name);
}
+/**
+ * CAMEL_SESSION_BOOK_UID_ANY:
+ *
+ * Can be used with camel_session_addressbook_contains_sync() as the book UID,
+ * meaning to check in all configured address books.
+ *
+ * Since: 3.44
+ **/
+
+/**
+ * CAMEL_SESSION_BOOK_UID_COMPLETION:
+ *
+ * Can be used with camel_session_addressbook_contains_sync() as the book UID,
+ * meaning to check in all address books enabled for auto-completion.
+ *
+ * Since: 3.44
+ **/
+
+/**
+ * camel_session_addressbook_contains_sync:
+ * @session: a #CamelSession
+ * @book_uid: an address book UID
+ * @email_address: an email address to check for
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Look up in an address book @book_uid for an address @email_address
+ * and returns whether any such contact exists.
+ *
+ * The @book_uid can be either one of the special constants
+ * %CAMEL_SESSION_BOOK_UID_ANY or %CAMEL_SESSION_BOOK_UID_COMPLETION,
+ * or it can be a UID of a configured address book.
+ *
+ * The @email_address can contain multiple addresses, then the function
+ * checks whether any of the given addresses is in the address book.
+ *
+ * Returns: %TRUE, when the @email_address could be found in the @book_uid
+ *
+ * Since: 3.44
+ **/
+gboolean
+camel_session_addressbook_contains_sync (CamelSession *session,
+ const gchar *book_uid,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelSessionClass *klass;
+
+ g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
+ g_return_val_if_fail (book_uid != NULL, FALSE);
+ g_return_val_if_fail (email_address != NULL, FALSE);
+
+ klass = CAMEL_SESSION_GET_CLASS (session);
+ g_return_val_if_fail (klass != NULL, FALSE);
+
+ if (!klass->addressbook_contains_sync)
+ return FALSE;
+
+ return klass->addressbook_contains_sync (session, book_uid, email_address, cancellable, error);
+}
+
/**
* camel_session_get_online:
* @session: a #CamelSession
diff --git a/src/camel/camel-session.h b/src/camel/camel-session.h
index 31a4fc553..897225da1 100644
--- a/src/camel/camel-session.h
+++ b/src/camel/camel-session.h
@@ -54,6 +54,9 @@
G_BEGIN_DECLS
+#define CAMEL_SESSION_BOOK_UID_ANY "*any"
+#define CAMEL_SESSION_BOOK_UID_COMPLETION "*completion"
+
typedef struct _CamelSession CamelSession;
typedef struct _CamelSessionClass CamelSessionClass;
typedef struct _CamelSessionPrivate CamelSessionPrivate;
@@ -146,9 +149,15 @@ struct _CamelSessionClass {
GSList **out_certificates, /* gchar * */
GCancellable *cancellable,
GError **error);
+ gboolean (*addressbook_contains_sync)
+ (CamelSession *session,
+ const gchar *book_uid,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error);
/* Padding for future expansion */
- gpointer reserved_methods[19];
+ gpointer reserved_methods[18];
/* Signals */
void (*job_started) (CamelSession *session,
@@ -239,6 +248,12 @@ void camel_session_set_junk_headers (CamelSession *session,
gint len);
gboolean camel_session_lookup_addressbook (CamelSession *session,
const gchar *name);
+gboolean camel_session_addressbook_contains_sync
+ (CamelSession *session,
+ const gchar *book_uid,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error);
gboolean camel_session_authenticate_sync (CamelSession *session,
CamelService *service,
diff --git a/src/private/org.gnome.evolution.dataserver.AddressBook.xml
b/src/private/org.gnome.evolution.dataserver.AddressBook.xml
index 61c645ba0..b3d16d30d 100644
--- a/src/private/org.gnome.evolution.dataserver.AddressBook.xml
+++ b/src/private/org.gnome.evolution.dataserver.AddressBook.xml
@@ -83,4 +83,9 @@
<arg name="object_path" direction="out" type="o"/>
</method>
+ <method name="ContainsEmail">
+ <arg name="email" direction="in" type="s"/>
+ <arg name="result" direction="out" type="b"/>
+ </method>
+
</interface>
diff --git a/tests/libedata-book/test-book-meta-backend.c b/tests/libedata-book/test-book-meta-backend.c
index b3fdddd6e..dd7ca91c2 100644
--- a/tests/libedata-book/test-book-meta-backend.c
+++ b/tests/libedata-book/test-book-meta-backend.c
@@ -1750,6 +1750,46 @@ test_cursor (EBookMetaBackend *meta_backend)
g_assert (success);
}
+static void
+test_contains_email (EBookMetaBackend *meta_backend)
+{
+ EBookBackendSyncClass *backend_sync_class;
+ gboolean success;
+ GError *error = NULL;
+
+ g_assert_nonnull (meta_backend);
+
+ backend_sync_class = E_BOOK_BACKEND_SYNC_GET_CLASS (meta_backend);
+ g_return_if_fail (backend_sync_class != NULL);
+ g_return_if_fail (backend_sync_class->contains_email_sync != NULL);
+
+ success = backend_sync_class->contains_email_sync (E_BOOK_BACKEND_SYNC (meta_backend),
+ "bobby brown com", NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ success = backend_sync_class->contains_email_sync (E_BOOK_BACKEND_SYNC (meta_backend),
+ "\"Bobby\" <bobby brown org>", NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ success = backend_sync_class->contains_email_sync (E_BOOK_BACKEND_SYNC (meta_backend),
+ "\"Unknown\" <unknown@no.where>", NULL, &error);
+ g_assert_no_error (error);
+ g_assert (!success);
+
+ success = backend_sync_class->contains_email_sync (E_BOOK_BACKEND_SYNC (meta_backend),
+ "\"Unknown\" <unknown@no.where>, \"Bobby\" <bobby brown org>", NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ success = backend_sync_class->contains_email_sync (E_BOOK_BACKEND_SYNC (meta_backend),
+ "", NULL, &error);
+ g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_CONSTRAINT);
+ g_assert (!success);
+ g_clear_error (&error);
+}
+
typedef void (* TestWithMainLoopFunc) (EBookMetaBackend *meta_backend);
typedef struct _MainLoopThreadData {
@@ -1843,6 +1883,7 @@ main_loop_wrapper (test_get_contact_list)
main_loop_wrapper (test_get_contact_list_uids)
main_loop_wrapper (test_refresh)
main_loop_wrapper (test_cursor)
+main_loop_wrapper (test_contains_email)
#undef main_loop_wrapper
@@ -1899,6 +1940,8 @@ main (gint argc,
tcu_fixture_setup, test_refresh_tcu, tcu_fixture_teardown);
g_test_add ("/EBookMetaBackend/Cursor", TCUFixture, &closure,
tcu_fixture_setup, test_cursor_tcu, tcu_fixture_teardown);
+ g_test_add ("/EBookMetaBackend/ContainsEmail", TCUFixture, &closure,
+ tcu_fixture_setup, test_contains_email_tcu, tcu_fixture_teardown);
res = g_test_run ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]