commit 8672ca1d2ae93056a097b6cfbfcfdf1547e2cb37
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Sun Jun 26 12:05:54 2011 -0400

    Handle fields-of-interest for local addressbook backend.
    
    This patch refactors the local addressbook backend to use
    the new sqlitedb api instead of the old summary apis.
    The result is that vcards are virtually built from the
    sqlitedb cache when fields-of-interest is set.
    
    Additionally, the patch adds the EBookBackend->notify_update()
    vfunc which can be implemented for e_book_backend_notify_update().
    The file backend uses this to notify with virtually created vcards
    from the sqlite cache in response to contact additions and modifications.
    
    This patch should address both bugs:
      https://bugzilla.gnome.org/show_bug.cgi?id=652179
      https://bugzilla.gnome.org/show_bug.cgi?id=652172

diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index 596c829..e2d88db 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -48,7 +48,7 @@
 #include "libebook/e-contact.h"
 
 #include "libedata-book/e-book-backend-sexp.h"
-#include "libedata-book/e-book-backend-summary.h"
+#include "libedata-book/e-book-backend-sqlitedb.h"
 #include "libedata-book/e-data-book.h"
 #include "libedata-book/e-data-book-view.h"
 
@@ -62,7 +62,11 @@
 #define E_BOOK_BACKEND_FILE_VERSION "0.2"
 
 #define PAS_ID_PREFIX "pas-id-"
-#define SUMMARY_FLUSH_TIMEOUT 5000
+
+#define SQLITEDB_EMAIL_ID    "addressbook@localbackend.com"
+#define SQLITEDB_FOLDER_ID   "folder_id"
+#define SQLITEDB_FOLDER_NAME "folder"
+
 
 #define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL)
 #define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg)
@@ -72,10 +76,11 @@ G_DEFINE_TYPE (EBookBackendFile, e_book_backend_file, E_TYPE_BOOK_BACKEND_SYNC)
 struct _EBookBackendFilePrivate {
 	gchar     *dirname;
 	gchar     *filename;
-	gchar     *summary_filename;
-	DB       *file_db;
-	DB_ENV   *env;
-	EBookBackendSummary *summary;
+	DB        *file_db;
+	DB_ENV    *env;
+
+	EBookBackendSqliteDB *sqlitedb;
+
 	/* for future use */
 	gpointer reserved1;
 	gpointer reserved2;
@@ -120,7 +125,7 @@ string_to_dbt (const gchar *str, DBT *dbt)
 }
 
 static EContact*
-create_contact (gchar *uid, const gchar *vcard)
+create_contact (const gchar *uid, const gchar *vcard)
 {
 	EContact *contact = e_contact_new_from_vcard (vcard);
 	if (!e_contact_get_const (contact, E_CONTACT_UID))
@@ -130,12 +135,14 @@ create_contact (gchar *uid, const gchar *vcard)
 }
 
 static gboolean
-build_summary (EBookBackendFilePrivate *bfpriv)
+build_sqlitedb (EBookBackendFilePrivate *bfpriv)
 {
 	DB             *db = bfpriv->file_db;
 	DBC            *dbc;
 	gint            db_error;
-	DBT  id_dbt, vcard_dbt;
+	DBT             id_dbt, vcard_dbt;
+	GSList         *contacts = NULL;
+	GError         *error = NULL;
 
 	db_error = db->cursor (db, NULL, &dbc, 0);
 
@@ -154,8 +161,8 @@ build_summary (EBookBackendFilePrivate *bfpriv)
 		if (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
 		    || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
 			EContact *contact = create_contact (id_dbt.data, vcard_dbt.data);
-			e_book_backend_summary_add_contact (bfpriv->summary, contact);
-			g_object_unref (contact);
+
+			contacts = g_slist_prepend (contacts, contact);
 		}
 
 		db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
@@ -164,6 +171,25 @@ build_summary (EBookBackendFilePrivate *bfpriv)
 
 	dbc->c_close (dbc);
 
+	contacts = g_slist_reverse (contacts);
+
+	if (!e_book_backend_sqlitedb_add_contacts (bfpriv->sqlitedb,
+						   SQLITEDB_FOLDER_ID,
+						   contacts, FALSE, &error)) {
+		g_warning ("Failed to build contact summary: %s", error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_slist_foreach (contacts, (GFunc)g_object_unref, NULL);
+	g_slist_free (contacts);
+
+	if (!e_book_backend_sqlitedb_set_is_populated (bfpriv->sqlitedb, SQLITEDB_FOLDER_ID, TRUE, &error)) {
+		g_warning ("Failed to set the sqlitedb populated flag: %s", error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
@@ -255,7 +281,14 @@ e_book_backend_file_create_contact (EBookBackendSync *backend,
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
 
 	if (do_create (bf, vcard, contact, perror)) {
-		e_book_backend_summary_add_contact (bf->priv->summary, *contact);
+		GError *error = NULL;
+
+		if (!e_book_backend_sqlitedb_add_contact (bf->priv->sqlitedb,
+							  SQLITEDB_FOLDER_ID,
+							  *contact, FALSE, &error)) {
+			g_warning ("Failed to add contact to summary: %s", error->message);
+			g_error_free (error);
+		}
 	}
 }
 
@@ -268,12 +301,13 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 				     GError **perror)
 {
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
-	DB             *db = bf->priv->file_db;
-	DBT            id_dbt;
-	gint            db_error;
-	const gchar    *id;
-	const GSList   *l;
-	GSList         *removed_cards = NULL;
+	DB               *db = bf->priv->file_db;
+	DBT               id_dbt;
+	gint              db_error;
+	const gchar      *id;
+	GSList           *removed_cards = NULL;
+	const GSList     *l;
+	GError           *error = NULL;
 
 	for (l = id_list; l; l = l->next) {
 		id = l->data;
@@ -297,12 +331,14 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 			g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
 	}
 
-	*ids = removed_cards;
-
-	for (l = removed_cards; l; l = l->next) {
-		id = l->data;
-		e_book_backend_summary_remove_contact (bf->priv->summary, id);
+	if (!e_book_backend_sqlitedb_remove_contacts (bf->priv->sqlitedb,
+						      SQLITEDB_FOLDER_ID,
+						      removed_cards, &error)) {
+		g_warning ("Failed to remove contacts from the summary: %s", error->message);
+		g_error_free (error);
 	}
+
+	*ids = removed_cards;
 }
 
 static void
@@ -353,8 +389,19 @@ e_book_backend_file_modify_contact (EBookBackendSync *backend,
 		if (db_error != 0) {
 			g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
 		} else {
-			e_book_backend_summary_remove_contact (bf->priv->summary, id);
-			e_book_backend_summary_add_contact (bf->priv->summary, *contact);
+			GError *error = NULL;
+
+			if (!e_book_backend_sqlitedb_remove_contact (bf->priv->sqlitedb,
+								     SQLITEDB_FOLDER_ID,
+								     id, &error)) {
+				g_warning ("Failed to remove contact from the summary: %s", error->message);
+				g_error_free (error);
+			} else if (!e_book_backend_sqlitedb_add_contact (bf->priv->sqlitedb,
+									 SQLITEDB_FOLDER_ID,
+									 *contact, FALSE, &error)) {
+				g_warning ("Failed to add contact to summary: %s", error->message);
+				g_error_free (error);
+			}
 		}
 	} else {
 		g_warning (G_STRLOC ": db->put failed with %s", db_strerror(db_error));
@@ -412,36 +459,27 @@ e_book_backend_file_get_contact_list (EBookBackendSync *backend,
 	EBookBackendSExp *card_sexp = NULL;
 	gboolean search_needed;
 	const gchar *search = query;
-	GSList *contact_list = NULL;
+	GSList *contact_list = NULL, *l;
+	GSList *summary_list = NULL;
 
 	d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
-	if (e_book_backend_summary_is_summary_query (bf->priv->summary, search)) {
 
-		/* do a summary query */
-		GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, search);
-		gint i;
+	summary_list = e_book_backend_sqlitedb_search (bf->priv->sqlitedb,
+						       SQLITEDB_FOLDER_ID,
+						       search, NULL, NULL);
 
-		if (!ids) {
-			g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
-			return;
-		}
+	if (summary_list) {
 
-		for (i = 0; i < ids->len; i++) {
-			gchar *id = g_ptr_array_index (ids, i);
-			string_to_dbt (id, &id_dbt);
-			memset (&vcard_dbt, 0, sizeof (vcard_dbt));
-			vcard_dbt.flags = DB_DBT_MALLOC;
+		for (l = summary_list; l; l = l->next) {
+			EbSdbSearchData *data = l->data;
 
-			db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
-			if (db_error == 0) {
-				contact_list = g_slist_prepend (contact_list, vcard_dbt.data);
-			} else {
-				g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
-				db_error_to_gerror (db_error, perror);
-				break;
-			}
+			contact_list = g_slist_prepend (contact_list, data->vcard);
+			data->vcard  = NULL;
 		}
-		g_ptr_array_free (ids, TRUE);
+
+		g_slist_foreach (summary_list, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+		g_slist_free (summary_list);
+
 	} else {
 		search_needed = TRUE;
 		if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
@@ -522,24 +560,12 @@ e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend,
 	GSList *uids = NULL;
 
 	d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
-	if (e_book_backend_summary_is_summary_query (bf->priv->summary, search)) {
-		/* do a summary query */
-		GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, search);
-		gint i;
-
-		if (!ids) {
-			g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
-			return;
-		}
 
-		for (i = 0; i < ids->len; i++) {
-			gchar *id = g_ptr_array_index (ids, i);
+	uids = e_book_backend_sqlitedb_search_uids (bf->priv->sqlitedb,
+						    SQLITEDB_FOLDER_ID,
+						    search, NULL);
 
-			uids = g_slist_prepend (uids, g_strdup (id));
-		}
-
-		g_ptr_array_free (ids, TRUE);
-	} else {
+	if (!uids) {
 		search_needed = TRUE;
 		if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
 			search_needed = FALSE;
@@ -633,6 +659,18 @@ get_closure (EDataBookView *book_view)
 	return g_object_get_data (G_OBJECT (book_view), "EBookBackendFile.BookView::closure");
 }
 
+static void
+notify_update_vcard (EDataBookView *book_view,
+		     gboolean       prefiltered,
+		     const gchar   *id,
+		     gchar         *vcard)
+{
+	if (prefiltered)
+		e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard);
+	else
+		e_data_book_view_notify_update_vcard (book_view, vcard);
+}
+
 static gpointer
 book_view_thread (gpointer data)
 {
@@ -644,6 +682,8 @@ book_view_thread (gpointer data)
 	DBT id_dbt, vcard_dbt;
 	gint db_error;
 	gboolean allcontacts;
+	GSList *summary_list, *l;
+	GHashTable *fields_of_interest;
 
 	g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (data), NULL);
 
@@ -661,8 +701,9 @@ book_view_thread (gpointer data)
 	   when/if it's stopped */
 	e_data_book_view_ref (book_view);
 
-	db = bf->priv->file_db;
-	query = e_data_book_view_get_card_query (book_view);
+	db                 = bf->priv->file_db;
+	query              = e_data_book_view_get_card_query (book_view);
+	fields_of_interest = e_data_book_view_get_fields_of_interest (book_view);
 
 	if ( !strcmp (query, "(contains \"x-evolution-any-field\" \"\")")) {
 		e_data_book_view_notify_progress (book_view, -1, _("Loading..."));
@@ -675,37 +716,21 @@ book_view_thread (gpointer data)
 	d(printf ("signalling parent thread\n"));
 	e_flag_set (closure->running);
 
-	if (e_book_backend_summary_is_summary_query (bf->priv->summary, query)) {
-		/* do a summary query */
-		GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, e_data_book_view_get_card_query (book_view));
-		gint i;
-
-		if (!ids)
-			goto done;
-
-		for (i = 0; i < ids->len; i++) {
-			gchar *id = g_ptr_array_index (ids, i);
+	summary_list = e_book_backend_sqlitedb_search (bf->priv->sqlitedb,
+						       SQLITEDB_FOLDER_ID,
+						       query, fields_of_interest, NULL);
+	if (summary_list) {
 
-			if (!e_flag_is_set (closure->running))
-				break;
+		for (l = summary_list; l; l = l->next) {
+			EbSdbSearchData *data = l->data;
 
-			string_to_dbt (id, &id_dbt);
-			memset (&vcard_dbt, 0, sizeof (vcard_dbt));
-			vcard_dbt.flags = DB_DBT_MALLOC;
-
-			db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
-
-			if (db_error == 0) {
-				e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard_dbt.data);
-			}
-			else {
-				g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
-			}
+			notify_update_vcard (book_view, TRUE, data->uid, data->vcard);
+			data->vcard = NULL;
 		}
 
-		g_ptr_array_free (ids, TRUE);
-	}
-	else {
+		g_slist_foreach (summary_list, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+		g_slist_free (summary_list);
+	} else {
 		/* iterate over the db and do the query there */
 		DBC    *dbc;
 
@@ -724,10 +749,8 @@ book_view_thread (gpointer data)
 
 				/* don't include the version in the list of cards */
 				if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
-					if (allcontacts)
-						e_data_book_view_notify_update_prefiltered_vcard (book_view, id_dbt.data, vcard_dbt.data);
-					else
-						e_data_book_view_notify_update_vcard (book_view, vcard_dbt.data);
+					notify_update_vcard (book_view, allcontacts, 
+							     id_dbt.data, vcard_dbt.data);
 				} else {
 					g_free (vcard_dbt.data);
 				}
@@ -747,7 +770,7 @@ book_view_thread (gpointer data)
 		}
 
 	}
-done:
+
 	if (e_flag_is_set (closure->running))
 		e_data_book_view_notify_complete (book_view, NULL /* Success */);
 
@@ -997,14 +1020,13 @@ e_book_backend_file_open (EBookBackendSync       *backend,
 			  GError	       **perror)
 {
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
-	gchar           *dirname, *filename;
-	gboolean        readonly = TRUE;
-	ESource *source = e_book_backend_get_source (E_BOOK_BACKEND (backend));
-	gint             db_error;
-	DB *db;
-	DB_ENV *env;
-	time_t db_mtime;
-	struct stat sb;
+	gchar            *dirname, *filename;
+	gboolean          readonly = TRUE;
+	ESource          *source = e_book_backend_get_source (E_BOOK_BACKEND (backend));
+	gint              db_error;
+	DB               *db;
+	DB_ENV           *env;
+	GError           *local_error = NULL;
 
 #ifdef CREATE_DEFAULT_VCARD
 	gboolean create_default_vcard = FALSE;
@@ -1200,23 +1222,25 @@ e_book_backend_file_open (EBookBackendSync       *backend,
 	bf->priv->dirname = dirname;
 	bf->priv->filename = filename;
 
-	if (g_stat (bf->priv->filename, &sb) == -1) {
-		db->close (db, 0);
-		bf->priv->file_db = NULL;
-		g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "stat(%s) failed", bf->priv->filename));
+	bf->priv->sqlitedb = e_book_backend_sqlitedb_new (bf->priv->dirname,
+							  SQLITEDB_EMAIL_ID,
+							  SQLITEDB_FOLDER_ID,
+							  SQLITEDB_FOLDER_NAME,
+							  FALSE,
+							  perror);
+	if (!bf->priv->sqlitedb)
 		return;
-	}
-	db_mtime = sb.st_mtime;
-
-	g_free (bf->priv->summary_filename);
-	bf->priv->summary_filename = g_strconcat (bf->priv->filename, ".summary", NULL);
-	bf->priv->summary = e_book_backend_summary_new (bf->priv->summary_filename, SUMMARY_FLUSH_TIMEOUT);
 
-	if (e_book_backend_summary_is_up_to_date (bf->priv->summary, db_mtime) == FALSE
-	    || e_book_backend_summary_load (bf->priv->summary) == FALSE ) {
-		if (!bf->priv->summary || !build_summary (bf->priv)) {
-			g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "Failed to build summary for an address book %s", bf->priv->filename));
+	if (!e_book_backend_sqlitedb_get_is_populated (bf->priv->sqlitedb,
+						       SQLITEDB_FOLDER_ID,
+						       &local_error)) {
+		if (local_error) {
+			g_propagate_error (perror, local_error);
 			return;
+		} else if (!build_sqlitedb (bf->priv)) {
+			g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+                                    "Failed to build summary for an address book %s",
+                                     bf->priv->filename));
 		}
 	}
 
@@ -1261,11 +1285,12 @@ e_book_backend_file_remove (EBookBackendSync *backend,
 		return;
 	}
 
-	/* unref the summary before we remove the file so it's not written out again */
-	g_object_unref (bf->priv->summary);
-	bf->priv->summary = NULL;
-	if (-1 == g_unlink (bf->priv->summary_filename))
-		g_warning ("failed to remove summary file `%s`: %s", bf->priv->summary_filename, g_strerror (errno));
+	if (!e_book_backend_sqlitedb_remove (bf->priv->sqlitedb, perror))
+		return;
+
+	/* unref the sqlitedb before we remove the file so it's not written out again */
+	g_object_unref (bf->priv->sqlitedb);
+	bf->priv->sqlitedb = NULL;
 
 	dir = g_dir_open (bf->priv->dirname, 0, NULL);
 	if (dir) {
@@ -1348,6 +1373,47 @@ e_book_backend_file_sync (EBookBackend *backend)
 	}
 }
 
+typedef struct {
+	EContact         *contact;
+	EBookBackendFile *bf;
+} NotifyData;
+
+static gboolean
+view_notify_update (EDataBookView *view, gpointer data)
+{
+	NotifyData *ndata    = data;
+	GHashTable *fields   = e_data_book_view_get_fields_of_interest (view);
+	gboolean    notified = FALSE;
+
+	if (e_book_backend_sqlitedb_is_summary_query (e_data_book_view_get_card_query (view), fields)) {
+
+		const gchar *uid = e_contact_get_const (ndata->contact, E_CONTACT_UID);
+		gchar       *vcard;
+
+		vcard = e_book_backend_sqlitedb_get_vcard_string (ndata->bf->priv->sqlitedb,
+								  SQLITEDB_FOLDER_ID, uid,
+								  fields, NULL);
+
+		if (vcard) {
+			e_data_book_view_notify_update_prefiltered_vcard (view, uid, vcard);
+			notified = TRUE;
+		}
+	}
+
+	if (!notified)
+		e_data_book_view_notify_update (view, ndata->contact);
+
+	return TRUE;
+}
+
+static void
+e_book_backend_file_notify_update (EBookBackend *backend, const EContact *contact)
+{
+	NotifyData data = { (EContact *)contact, E_BOOK_BACKEND_FILE (backend) };
+
+	e_book_backend_foreach_view (backend, view_notify_update, &data);
+}
+
 /**
  * e_book_backend_file_new:
  */
@@ -1377,9 +1443,9 @@ e_book_backend_file_dispose (GObject *object)
 	}
 	G_UNLOCK (global_env);
 
-	if (bf->priv->summary) {
-		g_object_unref (bf->priv->summary);
-		bf->priv->summary = NULL;
+	if (bf->priv->sqlitedb) {
+		g_object_unref (bf->priv->sqlitedb);
+		bf->priv->sqlitedb = NULL;
 	}
 
 	G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);
@@ -1394,7 +1460,6 @@ e_book_backend_file_finalize (GObject *object)
 
 	g_free (bf->priv->filename);
 	g_free (bf->priv->dirname);
-	g_free (bf->priv->summary_filename);
 
 	g_free (bf->priv);
 
@@ -1460,6 +1525,7 @@ e_book_backend_file_class_init (EBookBackendFileClass *klass)
 	backend_class->stop_book_view		= e_book_backend_file_stop_book_view;
 	backend_class->set_online		= e_book_backend_file_set_online;
 	backend_class->sync			= e_book_backend_file_sync;
+	backend_class->notify_update            = e_book_backend_file_notify_update;
 
 	sync_class->open_sync			= e_book_backend_file_open;
 	sync_class->remove_sync			= e_book_backend_file_remove;
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 358f489..72844dd 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -176,6 +176,20 @@ book_backend_finalize (GObject *object)
 	G_OBJECT_CLASS (e_book_backend_parent_class)->finalize (object);
 }
 
+static gboolean
+view_notify_update (EDataBookView *view, gpointer contact)
+{
+	e_data_book_view_notify_update (view, contact);
+
+	return TRUE;
+}
+
+static void
+book_backend_notify_update (EBookBackend *backend, const EContact *contact)
+{
+	e_book_backend_foreach_view (backend, view_notify_update, (gpointer) contact);
+}
+
 static void
 e_book_backend_class_init (EBookBackendClass *klass)
 {
@@ -191,6 +205,7 @@ e_book_backend_class_init (EBookBackendClass *klass)
 
 	klass->get_backend_property = book_backend_get_backend_property;
 	klass->set_backend_property = book_backend_set_backend_property;
+	klass->notify_update        = book_backend_notify_update;
 
 	g_object_class_install_property (
 		object_class,
@@ -1024,15 +1039,6 @@ e_book_backend_sync (EBookBackend *backend)
 	g_object_unref (backend);
 }
 
-
-
-static gboolean
-view_notify_update (EDataBookView *view, gpointer contact)
-{
-	e_data_book_view_notify_update (view, contact);
-
-	return TRUE;
-}
 
 /**
  * e_book_backend_notify_update:
@@ -1049,7 +1055,7 @@ view_notify_update (EDataBookView *view, gpointer contact)
 void
 e_book_backend_notify_update (EBookBackend *backend, const EContact *contact)
 {
-	e_book_backend_foreach_view (backend, view_notify_update, (gpointer) contact);
+	E_BOOK_BACKEND_GET_CLASS (backend)->notify_update (backend, contact);
 }
 
 static gboolean
diff --git a/addressbook/libedata-book/e-book-backend.h b/addressbook/libedata-book/e-book-backend.h
index a9f81d3..f6ff32f 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -79,6 +79,8 @@ struct _EBookBackendClass {
 	void	(* start_book_view)		(EBookBackend *backend, EDataBookView *book_view);
 	void	(* stop_book_view)		(EBookBackend *backend, EDataBookView *book_view);
 
+	void    (* notify_update)               (EBookBackend *backend, const EContact *contact);
+
 	/* Notification signals */
 	void	(* last_client_gone)		(EBookBackend *backend);
 	void	(* sync)			(EBookBackend *backend);
