[evolution-data-server] Clean up the Google Contacts addressbook backend



commit 42d8a30a497df386955a198538a80eb780ec2386
Author: Philip Withnall <philip tecnocode co uk>
Date:   Tue Apr 20 00:31:55 2010 +0100

    Clean up the Google Contacts addressbook backend
    
    Merge the entirely redundant GoogleBook into EBookBackendGoogle and simplify
    things a bit.

 addressbook/backends/google/Makefile.am            |   34 +-
 .../backends/google/e-book-backend-google.c        | 1000 +++++++++++++++---
 addressbook/backends/google/google-book.c          | 1138 --------------------
 addressbook/backends/google/google-book.h          |   75 --
 po/POTFILES.in                                     |    1 -
 5 files changed, 880 insertions(+), 1368 deletions(-)
---
diff --git a/addressbook/backends/google/Makefile.am b/addressbook/backends/google/Makefile.am
index a54ceb9..c7912de 100644
--- a/addressbook/backends/google/Makefile.am
+++ b/addressbook/backends/google/Makefile.am
@@ -2,31 +2,29 @@ extension_LTLIBRARIES = libebookbackendgoogle.la
 
 libebookbackendgoogle_la_CPPFLAGS = \
 	$(AM_CPPFLAGS)					\
-	-DG_LOG_DOMAIN=\"libebookbackendgoogle\" 	\
-	-I$(top_srcdir) 				\
-	-I$(top_builddir) 				\
-	-I$(top_srcdir)/addressbook 			\
-	-I$(top_builddir)/addressbook 			\
-	$(SOUP_CFLAGS) 					\
+	-DG_LOG_DOMAIN=\"libebookbackendgoogle\"	\
+	-I$(top_srcdir)					\
+	-I$(top_builddir)				\
+	-I$(top_srcdir)/addressbook			\
+	-I$(top_builddir)/addressbook			\
+	$(SOUP_CFLAGS)					\
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS)			\
 	$(GDATA_CFLAGS)
 
 libebookbackendgoogle_la_SOURCES = \
-	util.h \
-	util.c \
-	google-book.h \
-	google-book.c \
-	e-book-backend-google-factory.c \
-	e-book-backend-google.h \
+	util.h				\
+	util.c				\
+	e-book-backend-google-factory.c	\
+	e-book-backend-google.h		\
 	e-book-backend-google.c
 
 libebookbackendgoogle_la_LIBADD = \
-	$(top_builddir)/addressbook/libebook/libebook-1.2.la \
-	$(top_builddir)/addressbook/libedata-book/libedata-book-1.2.la \
-	$(top_builddir)/libedataserver/libedataserver-1.2.la \
-	$(top_builddir)/libebackend/libebackend-1.2.la \
-	$(SOUP_LIBS) \
-	$(EVOLUTION_ADDRESSBOOK_LIBS) \
+	$(top_builddir)/addressbook/libebook/libebook-1.2.la		\
+	$(top_builddir)/addressbook/libedata-book/libedata-book-1.2.la	\
+	$(top_builddir)/libedataserver/libedataserver-1.2.la		\
+	$(top_builddir)/libebackend/libebackend-1.2.la			\
+	$(SOUP_LIBS)							\
+	$(EVOLUTION_ADDRESSBOOK_LIBS)					\
 	$(GDATA_LIBS)
 
 libebookbackendgoogle_la_LDFLAGS = \
diff --git a/addressbook/backends/google/e-book-backend-google.c b/addressbook/backends/google/e-book-backend-google.c
index 6f498a1..a5d5442 100644
--- a/addressbook/backends/google/e-book-backend-google.c
+++ b/addressbook/backends/google/e-book-backend-google.c
@@ -24,40 +24,603 @@
 #include <string.h>
 #include <errno.h>
 
+#include <libedataserver/e-proxy.h>
 #include <libebook/e-contact.h>
 #include <libedata-book/e-data-book.h>
 #include <libedata-book/e-data-book-view.h>
 #include <libedata-book/e-book-backend-sexp.h>
+#include <libedata-book/e-book-backend-cache.h>
 #include <gdata/gdata-service.h>
+#include <gdata/services/contacts/gdata-contacts-service.h>
+#include <gdata/services/contacts/gdata-contacts-query.h>
+#include <gdata/services/contacts/gdata-contacts-contact.h>
 
 #include "e-book-backend-google.h"
-#include "google-book.h"
 #include "util.h"
 
+#define URI_GET_CONTACTS "://www.google.com/m8/feeds/contacts/default/full"
+
 G_DEFINE_TYPE (EBookBackendGoogle, e_book_backend_google, E_TYPE_BOOK_BACKEND_SYNC)
 
+typedef enum {
+	NO_CACHE,
+	ON_DISK_CACHE,
+	IN_MEMORY_CACHE
+} CacheType;
+
 struct _EBookBackendGooglePrivate {
 	GNOME_Evolution_Addressbook_BookMode mode;
-	GoogleBook *book;
 	GList *bookviews;
-};
 
-#define GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE ((obj), E_TYPE_BOOK_BACKEND_GOOGLE, EBookBackendGooglePrivate))
+	gchar *username;
+	CacheType cache_type;
+	union {
+		EBookBackendCache *on_disk;
+		struct {
+			GHashTable *contacts;
+			GHashTable *gdata_entries;
+			GTimeVal last_updated;
+		} in_memory;
+	} cache;
+
+	gboolean offline;
+	GDataService *service;
+	EProxy *proxy;
+	guint refresh_interval;
+	gboolean use_ssl;
+
+	/* Whether the backend is being used by a view at the moment; if it isn't, we don't need to do updates or send out notifications */
+	gboolean live_mode;
+
+	/* In live mode we will send out signals in an idle_handler */
+	guint idle_id;
+
+	guint refresh_id;
+};
 
 gboolean __e_book_backend_google_debug__;
 
-static EBookBackendSyncStatus e_book_backend_status_from_google_book_error (GError *error);
+static EBookBackendSyncStatus e_book_backend_status_from_gdata_error (GError *error);
+
+static void
+cache_init (EBookBackend *backend, gboolean on_disk)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+	if (on_disk) {
+		priv->cache_type = ON_DISK_CACHE;
+		priv->cache.on_disk = e_book_backend_cache_new (priv->username);
+	} else {
+		priv->cache_type = IN_MEMORY_CACHE;
+		priv->cache.in_memory.contacts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+		priv->cache.in_memory.gdata_entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+		memset (&priv->cache.in_memory.last_updated, 0, sizeof (GTimeVal));
+	}
+}
+
+static EContact *
+cache_add_contact (EBookBackend *backend, GDataEntry *entry)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	EContact *contact;
+	const gchar *uid;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		contact = _e_contact_new_from_gdata_entry (entry);
+		_e_contact_add_gdata_entry_xml (contact, entry);
+		e_book_backend_cache_add_contact (priv->cache.on_disk, contact);
+		_e_contact_remove_gdata_entry_xml (contact);
+		return contact;
+	case IN_MEMORY_CACHE:
+		contact = _e_contact_new_from_gdata_entry (entry);
+		uid = e_contact_get_const (contact, E_CONTACT_UID);
+		g_hash_table_insert (priv->cache.in_memory.contacts, g_strdup (uid), g_object_ref (contact));
+		g_hash_table_insert (priv->cache.in_memory.gdata_entries, g_strdup (uid), g_object_ref (entry));
+		return contact;
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static gboolean
+cache_remove_contact (EBookBackend *backend, const gchar *uid)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	gboolean success = TRUE;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		return e_book_backend_cache_remove_contact (priv->cache.on_disk, uid);
+	case IN_MEMORY_CACHE:
+		success = g_hash_table_remove (priv->cache.in_memory.contacts, uid);
+		return success && g_hash_table_remove (priv->cache.in_memory.gdata_entries, uid);
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+cache_has_contact (EBookBackend *backend, const gchar *uid)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		return e_book_backend_cache_check_contact (priv->cache.on_disk, uid);
+	case IN_MEMORY_CACHE:
+		return g_hash_table_lookup (priv->cache.in_memory.contacts, uid) ? TRUE : FALSE;
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	return FALSE;
+}
+
+static EContact *
+cache_get_contact (EBookBackend *backend, const gchar *uid, GDataEntry **entry)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	EContact *contact;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		contact = e_book_backend_cache_get_contact (priv->cache.on_disk, uid);
+		if (contact) {
+			if (entry) {
+				const gchar *entry_xml, *edit_link;
+
+				entry_xml = _e_contact_get_gdata_entry_xml (contact, &edit_link);
+				*entry = GDATA_ENTRY (gdata_parsable_new_from_xml (GDATA_TYPE_CONTACTS_CONTACT, entry_xml, -1, NULL));
+
+				if (*entry) {
+					GDataLink *link = gdata_link_new (edit_link, GDATA_LINK_EDIT);
+					gdata_entry_add_link (*entry, link);
+					g_object_unref (link);
+				}
+			}
+
+			_e_contact_remove_gdata_entry_xml (contact);
+		}
+		return contact;
+	case IN_MEMORY_CACHE:
+		contact = g_hash_table_lookup (priv->cache.in_memory.contacts, uid);
+		if (entry) {
+			*entry = g_hash_table_lookup (priv->cache.in_memory.gdata_entries, uid);
+			if (*entry)
+				g_object_ref (*entry);
+		}
+
+		if (contact)
+			g_object_ref (contact);
+
+		return contact;
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static GList *
+_g_hash_table_to_list (GHashTable *ht)
+{
+	GList *l = NULL;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	g_hash_table_iter_init (&iter, ht);
+	while (g_hash_table_iter_next (&iter, &key, &value))
+		l = g_list_prepend (l, g_object_ref (G_OBJECT (value)));
+
+	l = g_list_reverse (l);
+
+	return l;
+}
+
+static GList *
+cache_get_contacts (EBookBackend *backend)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	GList *contacts, *iter;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		contacts = e_book_backend_cache_get_contacts (priv->cache.on_disk, "(contains \"x-evolution-any-field\" \"\")");
+		for (iter = contacts; iter; iter = iter->next)
+			_e_contact_remove_gdata_entry_xml (iter->data);
+
+		return contacts;
+	case IN_MEMORY_CACHE:
+		return _g_hash_table_to_list (priv->cache.in_memory.contacts);
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static void
+cache_freeze (EBookBackend *backend)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+	if (priv->cache_type == ON_DISK_CACHE)
+		e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache.on_disk));
+}
+
+static void
+cache_thaw (EBookBackend *backend)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+	if (priv->cache_type == ON_DISK_CACHE)
+		e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache.on_disk));
+}
+
+static gchar *
+cache_get_last_update (EBookBackend *backend)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		return e_book_backend_cache_get_time (priv->cache.on_disk);
+	case IN_MEMORY_CACHE:
+		if (priv->cache.in_memory.contacts)
+			return g_time_val_to_iso8601 (&priv->cache.in_memory.last_updated);
+		break;
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static gboolean
+cache_get_last_update_tv (EBookBackend *backend, GTimeVal *tv)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	gchar *last_update;
+	gint rv;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		last_update = e_book_backend_cache_get_time (priv->cache.on_disk);
+		rv = last_update ? g_time_val_from_iso8601 (last_update, tv) : FALSE;
+		g_free (last_update);
+		return rv;
+	case IN_MEMORY_CACHE:
+		memcpy (tv, &priv->cache.in_memory.last_updated, sizeof (GTimeVal));
+		return priv->cache.in_memory.contacts != NULL;
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	return FALSE;
+}
+
+static void
+cache_set_last_update (EBookBackend *backend, GTimeVal *tv)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	gchar *_time;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		_time = g_time_val_to_iso8601 (tv);
+		/* Work around a bug in EBookBackendCache */
+		e_file_cache_remove_object (E_FILE_CACHE (priv->cache.on_disk), "last_update_time");
+		e_book_backend_cache_set_time (priv->cache.on_disk, _time);
+		g_free (_time);
+		return;
+	case IN_MEMORY_CACHE:
+		memcpy (&priv->cache.in_memory.last_updated, tv, sizeof (GTimeVal));
+	case NO_CACHE:
+	default:
+		break;
+	}
+}
+
+static gboolean
+cache_needs_update (EBookBackend *backend, guint *remaining_secs)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	GTimeVal last, current;
+	guint diff;
+	gboolean rv;
+
+	if (remaining_secs)
+		*remaining_secs = G_MAXUINT;
+
+	/* We never want to update in offline mode */
+	if (priv->offline)
+		return FALSE;
+
+	rv = cache_get_last_update_tv (backend, &last);
+
+	if (!rv)
+		return TRUE;
+
+	g_get_current_time (&current);
+	if (last.tv_sec > current.tv_sec) {
+		g_warning ("last update is in the feature?");
+
+		/* Do an update so we can fix this */
+		return TRUE;
+	}
+	diff = current.tv_sec - last.tv_sec;
+
+	if (diff >= priv->refresh_interval)
+		return TRUE;
+
+	if (remaining_secs)
+		*remaining_secs = priv->refresh_interval - diff;
+
+	__debug__ ("No update needed. Next update needed in %d secs", priv->refresh_interval - diff);
+
+	return FALSE;
+}
+
+static void
+on_contact_added (EBookBackend *backend, EContact *contact)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	GList *iter;
+
+	if (!priv->live_mode)
+		return;
+
+	for (iter = priv->bookviews; iter; iter = iter->next)
+		e_data_book_view_notify_update (E_DATA_BOOK_VIEW (iter->data), g_object_ref (contact));
+}
+
+static void
+on_contact_removed (EBookBackend *backend, const gchar *uid)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	GList *iter;
+
+	if (!priv->live_mode)
+		return;
+
+	for (iter = priv->bookviews; iter; iter = iter->next)
+		e_data_book_view_notify_remove (E_DATA_BOOK_VIEW (iter->data), g_strdup (uid));
+}
+
+static void
+on_contact_changed (EBookBackend *backend, EContact *contact)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	GList *iter;
+
+	if (!priv->live_mode)
+		return;
+
+	for (iter = priv->bookviews; iter; iter = iter->next)
+		e_data_book_view_notify_update (E_DATA_BOOK_VIEW (iter->data), g_object_ref (contact));
+}
+
+static void
+on_sequence_complete (EBookBackend *backend, GError *error)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_Success;
+	GList *iter;
+
+	if (!priv->live_mode)
+		return;
+
+	if (error) {
+		status = e_book_backend_status_from_gdata_error (error);
+		__debug__ ("Book-view query failed: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	for (iter = priv->bookviews; iter; iter = iter->next)
+		e_data_book_view_notify_complete (E_DATA_BOOK_VIEW (iter->data), GNOME_Evolution_Addressbook_Success);
+}
+
+static void
+process_subsequent_entry (GDataEntry *entry, EBookBackend *backend)
+{
+	gboolean is_deleted, is_cached;
+	const gchar *uid;
+
+	__debug__ (G_STRFUNC);
+	uid = gdata_entry_get_id (entry);
+	is_deleted = gdata_contacts_contact_is_deleted (GDATA_CONTACTS_CONTACT (entry));
+
+	is_cached = cache_has_contact (backend, uid);
+	if (is_deleted) {
+		/* Do we have this item in our cache? */
+		if (is_cached) {
+			cache_remove_contact (backend, uid);
+			on_contact_removed (backend, uid);
+		}
+	} else {
+		EContact *contact = cache_add_contact (backend, entry);
+
+		if (is_cached)
+			on_contact_changed (backend, contact);
+		else
+			on_contact_added (backend, contact);
+
+		g_object_unref (contact);
+	}
+}
+
+static void
+process_initial_entry (GDataEntry *entry, EBookBackend *backend)
+{
+	EContact *contact;
+
+	__debug__ (G_STRFUNC);
+
+	contact = cache_add_contact (backend, entry);
+	on_contact_added (backend, contact);
+	g_object_unref (contact);
+}
+
+static gboolean
+get_new_contacts_in_chunks (EBookBackend *backend, gint chunk_size, GError **error)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	GDataFeed *feed;
+	GDataQuery *query;
+	gchar *last_updated;
+	GError *our_error = NULL;
+	gboolean rv = TRUE;
+	GTimeVal current_time;
+	int results;
+
+	__debug__ (G_STRFUNC);
+	g_return_val_if_fail (priv->service, FALSE);
+
+	last_updated = cache_get_last_update (backend);
+	cache_freeze (backend);
+
+	/* Build our query */
+	query = GDATA_QUERY (gdata_contacts_query_new_with_limits (NULL, 1, chunk_size));
+	if (last_updated) {
+		GTimeVal updated;
+
+		g_assert (g_time_val_from_iso8601 (last_updated, &updated) == TRUE);
+		gdata_query_set_updated_min (query, &updated);
+		gdata_contacts_query_set_show_deleted (GDATA_CONTACTS_QUERY (query), TRUE);
+	}
+
+	/* Get the paginated results */
+	do {
+		GList *entries;
+
+		/* Run the query */
+		feed = gdata_contacts_service_query_contacts (GDATA_CONTACTS_SERVICE (priv->service), query, NULL, NULL, NULL, &our_error);
+
+		if (our_error) {
+			on_sequence_complete (backend, our_error);
+			g_propagate_error (error, our_error);
+
+			rv = FALSE;
+			goto out;
+		}
+
+		entries = gdata_feed_get_entries (feed);
+		results = entries ? g_list_length (entries) : 0;
+		__debug__ ("Feed has %d entries", results);
+
+		/* Process the entries from this page */
+		if (last_updated)
+			g_list_foreach (entries, (GFunc) process_subsequent_entry, backend);
+		else
+			g_list_foreach (entries, (GFunc) process_initial_entry, backend);
+		g_object_unref (feed);
+
+		/* Move to the next page */
+		gdata_query_next_page (query);
+	} while (results == chunk_size);
+
+	/* Finish updating the cache */
+	g_get_current_time (&current_time);
+	cache_set_last_update (backend, &current_time);
+	on_sequence_complete (backend, NULL);
+
+out:
+	g_free (last_updated);
+	cache_thaw (backend);
+
+	return rv;
+}
+
+static gboolean cache_refresh_if_needed (EBookBackend *backend, GError **error);
+
+static gboolean
+on_refresh_timeout (EBookBackend *backend)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+	__debug__ (G_STRFUNC);
+
+	priv->refresh_id = 0;
+	if (priv->live_mode)
+		cache_refresh_if_needed (backend, NULL);
+
+	return FALSE;
+}
+
+static gboolean
+cache_refresh_if_needed (EBookBackend *backend, GError **error)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	guint remaining_secs;
+	gint rv = TRUE;
+	gboolean install_timeout;
+
+	__debug__ (G_STRFUNC);
+
+	if (priv->offline || !priv->service) {
+		__debug__ ("We are not connected to Google%s.", priv->offline ? " (offline mode)" : "");
+		return TRUE;
+	}
+
+	install_timeout = (priv->live_mode && priv->refresh_interval > 0 && 0 == priv->refresh_id);
+
+	if (cache_needs_update (backend, &remaining_secs)) {
+		rv = get_new_contacts_in_chunks (backend, 32, error);
+		if (install_timeout)
+			priv->refresh_id = g_timeout_add_seconds (priv->refresh_interval, (GSourceFunc) on_refresh_timeout, backend);
+	} else {
+		if (install_timeout) {
+			__debug__ ("Installing timeout with %d seconds", remaining_secs);
+			priv->refresh_id = g_timeout_add_seconds (remaining_secs, (GSourceFunc) on_refresh_timeout, backend);
+		}
+	}
+	return rv;
+}
+
+static void
+cache_destroy (EBookBackend *backend)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+	switch (priv->cache_type) {
+	case ON_DISK_CACHE:
+		g_object_unref (priv->cache.on_disk);
+		break;
+	case IN_MEMORY_CACHE:
+		g_hash_table_destroy (priv->cache.in_memory.contacts);
+		g_hash_table_destroy (priv->cache.in_memory.gdata_entries);
+		break;
+	case NO_CACHE:
+	default:
+		break;
+	}
+
+	priv->cache_type = NO_CACHE;
+}
 
 static EBookBackendSyncStatus
 e_book_backend_google_create_contact (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *vcard_str, EContact **out_contact)
 {
-	EBookBackendGooglePrivate *priv;
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_OtherError;
 	EContact *contact;
 	GError *error = NULL;
+	GDataEntry *entry, *new_entry;
+	gchar *xml;
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
 	__debug__ ("Creating: %s", vcard_str);
 	*out_contact = NULL;
@@ -65,53 +628,92 @@ e_book_backend_google_create_contact (EBookBackendSync *backend, EDataBook *book
 	if (priv->mode != GNOME_Evolution_Addressbook_MODE_REMOTE)
 		return GNOME_Evolution_Addressbook_OfflineUnavailable;
 
+	g_return_val_if_fail (priv->service, status);
+
+	/* Build the GDataEntry from the vCard */
 	contact = e_contact_new_from_vcard (vcard_str);
-	google_book_add_contact (priv->book, contact, out_contact, &error);
+	entry = _gdata_entry_new_from_e_contact (contact);
 	g_object_unref (contact);
 
-	if (error) {
-		status = e_book_backend_status_from_google_book_error (error);
+	/* Debug XML output */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (entry));
+	__debug__ ("new entry with xml: %s", xml);
+	g_free (xml);
+
+	/* Insert the entry on the server */
+	new_entry = GDATA_ENTRY (gdata_contacts_service_insert_contact (GDATA_CONTACTS_SERVICE (priv->service), GDATA_CONTACTS_CONTACT (entry),
+	                                                                NULL, &error));
+	g_object_unref (entry);
+
+	if (!new_entry) {
+		status = e_book_backend_status_from_gdata_error (error);
 		__debug__ ("Creating contact failed: %s", error->message);
-		g_clear_error (&error);
-		*out_contact = NULL;
+		g_error_free (error);
+
 		return status;
 	}
 
+	/* Add the new contact to the cache */
+	*out_contact = cache_add_contact (E_BOOK_BACKEND (backend), new_entry);
+	g_object_unref (new_entry);
+
 	return GNOME_Evolution_Addressbook_Success;
 }
 
 static EBookBackendSyncStatus
 e_book_backend_google_remove_contacts (EBookBackendSync *backend, EDataBook *book, guint32 opid, GList *id_list, GList **ids)
 {
-	EBookBackendGooglePrivate *priv;
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_OtherError;
 	GList *id_iter;
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
 	*ids = NULL;
 
 	if (priv->mode != GNOME_Evolution_Addressbook_MODE_REMOTE)
 		return GNOME_Evolution_Addressbook_OfflineUnavailable;
 
+	g_return_val_if_fail (priv->service, status);
+
 	for (id_iter = id_list; id_iter; id_iter = id_iter->next) {
 		GError *error = NULL;
 		const gchar *uid;
+		GDataEntry *entry = NULL;
+		EContact *cached_contact;
 
+		/* Get the contact and associated GDataEntry from the cache */
 		uid = id_iter->data;
-		google_book_remove_contact (priv->book, uid, &error);
+		cached_contact = cache_get_contact (E_BOOK_BACKEND (backend), uid, &entry);
 
-		if (error) {
+		if (!cached_contact) {
+			/* Only the last error will be reported */
+			status = GNOME_Evolution_Addressbook_ContactNotFound;
+			__debug__ ("Deleting contact %s failed: Contact not found in cache.", uid);
+
+			continue;
+		}
+
+		g_object_unref (cached_contact);
+
+		/* Remove the contact from the cache */
+		cache_remove_contact (E_BOOK_BACKEND (backend), uid);
+
+		/* Delete the contact from the server */
+		if (!gdata_service_delete_entry (GDATA_SERVICE (priv->service), entry, NULL, &error)) {
 			/* Only last error will be reported */
-			status = e_book_backend_status_from_google_book_error (error);
+			status = e_book_backend_status_from_gdata_error (error);
 			__debug__ ("Deleting contact %s failed: %s", uid, error->message);
-			g_clear_error (&error);
+			g_error_free (error);
 		} else {
+			/* Success! */
 			*ids = g_list_append (*ids, g_strdup (uid));
 		}
+
+		g_object_unref (entry);
 	}
 
+	/* On error, return the status of the last error */
 	if (!*ids)
 		return status;
 
@@ -121,13 +723,15 @@ e_book_backend_google_remove_contacts (EBookBackendSync *backend, EDataBook *boo
 static EBookBackendSyncStatus
 e_book_backend_google_modify_contact (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *vcard_str, EContact **out_contact)
 {
-	EBookBackendGooglePrivate *priv;
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_OtherError;
-	EContact *contact;
+	EContact *contact, *cached_contact;
 	GError *error = NULL;
+	GDataEntry *entry = NULL, *new_entry;
+	gchar *xml;
+	const gchar *uid;
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
 	__debug__ ("Updating: %s", vcard_str);
 	*out_contact = NULL;
@@ -135,41 +739,89 @@ e_book_backend_google_modify_contact (EBookBackendSync *backend, EDataBook *book
 	if (priv->mode != GNOME_Evolution_Addressbook_MODE_REMOTE)
 		return GNOME_Evolution_Addressbook_OfflineUnavailable;
 
+	g_return_val_if_fail (priv->service, status);
+
+	/* Get the new contact and its UID */
 	contact = e_contact_new_from_vcard (vcard_str);
-	google_book_update_contact (priv->book, contact, out_contact, &error);
+	uid = e_contact_get (contact, E_CONTACT_UID);
+
+	/* Get the old cached contact with the same UID and its associated GDataEntry */
+	cached_contact = cache_get_contact (E_BOOK_BACKEND (backend), uid, &entry);
+
+	if (!cached_contact) {
+		__debug__ ("Modifying contact failed: Contact with uid %s not found in cache.", uid);
+		g_object_unref (contact);
+
+		return GNOME_Evolution_Addressbook_ContactNotFound;
+	}
+
+	g_object_unref (cached_contact);
+
+	/* Update the old GDataEntry from the new contact */
+	_gdata_entry_update_from_e_contact (entry, contact);
 	g_object_unref (contact);
 
-	if (error) {
-		status = e_book_backend_status_from_google_book_error (error);
+	/* Output debug XML */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (entry));
+	__debug__ ("Before:\n%s", xml);
+	g_free (xml);
+
+	/* Update the contact on the server */
+	new_entry = GDATA_ENTRY (gdata_contacts_service_update_contact (GDATA_CONTACTS_SERVICE (priv->service), GDATA_CONTACTS_CONTACT (entry),
+	                                                                NULL, &error));
+	g_object_unref (entry);
+
+	if (!new_entry) {
+		status = e_book_backend_status_from_gdata_error (error);
 		__debug__ ("Modifying contact failed: %s", error->message);
-		g_clear_error (&error);
-		*out_contact = NULL;
+		g_error_free (error);
+
 		return status;
 	}
 
+	/* Output debug XML */
+	xml = NULL;
+	if (new_entry)
+		xml = gdata_parsable_get_xml (GDATA_PARSABLE (new_entry));
+	__debug__ ("After:\n%s", xml);
+	g_free (xml);
+
+	/* Add the new entry to the cache */
+	*out_contact = cache_add_contact (E_BOOK_BACKEND (backend), new_entry);
+	g_object_unref (new_entry);
+
 	return GNOME_Evolution_Addressbook_Success;
 }
 
 static EBookBackendSyncStatus
 e_book_backend_google_get_contact (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *uid, gchar **vcard_str)
 {
-	EBookBackendGooglePrivate *priv;
 	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_OtherError;
 	EContact *contact;
 	GError *error = NULL;
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
-	contact = google_book_get_contact (priv->book, uid, &error);
+	/* Refresh the cache */
+	cache_refresh_if_needed (E_BOOK_BACKEND (backend), &error);
 
 	if (error) {
-		status = e_book_backend_status_from_google_book_error (error);
+		status = e_book_backend_status_from_gdata_error (error);
 		__debug__ ("Getting contact with uid %s failed: %s", uid, error->message);
-		g_clear_error (&error);
+		g_error_free (error);
+
 		return status;
 	}
 
+	/* Get the contact */
+	contact = cache_get_contact (E_BOOK_BACKEND (backend), uid, NULL);
+
+	if (!contact) {
+		__debug__ ("Getting contact with uid %s failed: Contact not found in cache.", uid);
+		return GNOME_Evolution_Addressbook_ContactNotFound;
+	}
+
+	/* Success! Build and return a vCard of the contacts */
 	*vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
 	g_object_unref (contact);
 
@@ -179,188 +831,230 @@ e_book_backend_google_get_contact (EBookBackendSync *backend, EDataBook *book, g
 static EBookBackendSyncStatus
 e_book_backend_google_get_contact_list (EBookBackendSync *backend, EDataBook *book, guint32 opid, const gchar *query, GList **contacts)
 {
-	EBookBackendGooglePrivate *priv;
 	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_OtherError;
 	EBookBackendSExp *sexp;
 	GError *error = NULL;
 	GList *all_contacts;
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
 	*contacts = NULL;
 
-	all_contacts = google_book_get_all_contacts (priv->book, &error);
+	/* Refresh the cache */
+	cache_refresh_if_needed (E_BOOK_BACKEND (backend), &error);
 
 	if (error) {
-		status = e_book_backend_status_from_google_book_error (error);
+		status = e_book_backend_status_from_gdata_error (error);
 		__debug__ ("Getting all contacts failed: %s", error->message);
 		g_clear_error (&error);
+
 		return status;
 	}
 
+	/* Get all contacts */
 	sexp = e_book_backend_sexp_new (query);
-	while (all_contacts) {
-		EContact *contact;
+	all_contacts = cache_get_contacts (E_BOOK_BACKEND (backend));
 
-		contact = all_contacts->data;
+	for (; all_contacts; all_contacts = g_list_delete_link (all_contacts, all_contacts)) {
+		EContact *contact = all_contacts->data;
+
+		/* If the search expression matches the contact, include it in the search results */
 		if (e_book_backend_sexp_match_contact (sexp, contact)) {
 			gchar *vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
 			*contacts = g_list_append (*contacts, vcard_str);
 		}
-		g_object_unref (contact);
 
-		all_contacts = g_list_delete_link (all_contacts, all_contacts);
+		g_object_unref (contact);
 	}
+
 	g_object_unref (sexp);
 
 	return GNOME_Evolution_Addressbook_Success;
 }
 
-static void
-on_google_book_contact_added (GoogleBook *book, EContact *contact, gpointer user_data)
+static gboolean
+on_refresh_idle (EBookBackend *backend)
 {
-	EBookBackendGooglePrivate *priv = GET_PRIVATE (user_data);
-	GList *iter;
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 
-	for (iter = priv->bookviews; iter; iter = iter->next) {
-		g_object_ref (contact);
-		e_data_book_view_notify_update (E_DATA_BOOK_VIEW (iter->data), contact);
-	}
-}
+	priv->idle_id = 0;
+	cache_refresh_if_needed (backend, NULL);
 
-static void
-on_google_book_contact_removed (GoogleBook *book, const gchar *uid, gpointer user_data)
-{
-	EBookBackendGooglePrivate *priv = GET_PRIVATE (user_data);
-	GList *iter;
-
-	for (iter = priv->bookviews; iter; iter = iter->next)
-		e_data_book_view_notify_remove (E_DATA_BOOK_VIEW (iter->data), g_strdup (uid));
+	return FALSE;
 }
 
 static void
-on_google_book_contact_changed (GoogleBook *book, EContact *contact, gpointer user_data)
+set_live_mode (EBookBackend *backend, gboolean live_mode)
 {
-	EBookBackendGooglePrivate *priv = GET_PRIVATE (user_data);
-	GList *iter;
-
-	for (iter = priv->bookviews; iter; iter = iter->next) {
-		g_object_ref (contact);
-		e_data_book_view_notify_update (E_DATA_BOOK_VIEW (iter->data), contact);
-	}
-}
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 
-static void
-on_google_book_sequence_complete (GoogleBook *book, GError *error, gpointer user_data)
-{
-	EBookBackendGooglePrivate *priv = GET_PRIVATE (user_data);
-	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_Success;
-	GList *iter;
+	__debug__ (G_STRFUNC);
+	priv->live_mode = live_mode;
 
-	if (error) {
-		status = e_book_backend_status_from_google_book_error (error);
-		__debug__ ("Book-view query failed: %s", error->message);
-		g_clear_error (&error);
+	if (!live_mode && priv->refresh_id > 0) {
+		g_source_remove (priv->refresh_id);
+		priv->refresh_id = 0;
 	}
 
-	for (iter = priv->bookviews; iter; iter = iter->next)
-		e_data_book_view_notify_complete (E_DATA_BOOK_VIEW (iter->data), GNOME_Evolution_Addressbook_Success);
+	if (priv->live_mode)
+		cache_refresh_if_needed (backend, NULL);
 }
 
 static void
 e_book_backend_google_start_book_view (EBookBackend *backend, EDataBookView *bookview)
 {
-	EBookBackendGooglePrivate *priv;
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	GList *cached_contacts;
 
 	g_return_if_fail (E_IS_BOOK_BACKEND_GOOGLE (backend));
 	g_return_if_fail (E_IS_DATA_BOOK_VIEW (bookview));
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
 	priv->bookviews = g_list_append (priv->bookviews, bookview);
 
 	e_data_book_view_ref (bookview);
 	e_data_book_view_notify_status_message (bookview, "Loading...");
 
-	google_book_set_live_mode (priv->book, TRUE);
-	cached_contacts = google_book_get_all_contacts_in_live_mode (priv->book);
-	while (cached_contacts) {
-		EContact *contact = cached_contacts->data;
+	/* Ensure that we're ready to support a view */
+	set_live_mode (backend, TRUE);
+
+	/* Update the cache if necessary */
+	if (cache_needs_update (backend, NULL)) {
+		if (!priv->service) {
+			/* We need authorization first */
+			e_book_backend_notify_auth_required (backend);
+		} else {
+			/* Update in an idle function, so that this call doesn't block */
+			priv->idle_id = g_idle_add ((GSourceFunc) on_refresh_idle, backend);
+		}
+	}
 
+	/* Get the contacts */
+	cached_contacts = cache_get_contacts (backend);
+	__debug__ ("%d contacts found in cache", g_list_length (cached_contacts));
+
+	/* Notify the view that all the contacts have changed (i.e. been added) */
+	for (; cached_contacts; cached_contacts = g_list_delete_link (cached_contacts, cached_contacts)) {
+		EContact *contact = cached_contacts->data;
 		e_data_book_view_notify_update (bookview, contact);
 		g_object_unref (contact);
-		cached_contacts = g_list_delete_link (cached_contacts, cached_contacts);
 	}
+
 	e_data_book_view_notify_complete (bookview, GNOME_Evolution_Addressbook_Success);
 }
 
 static void
 e_book_backend_google_stop_book_view (EBookBackend *backend, EDataBookView *bookview)
 {
-	EBookBackendGooglePrivate *priv;
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
+	/* Remove the view from the list of active views */
 	priv->bookviews = g_list_remove (priv->bookviews, bookview);
 	e_data_book_view_unref (bookview);
 
+	/* If there are no book views left, we can stop doing certain things, like refreshes */
 	if (!priv->bookviews)
-		google_book_set_live_mode (priv->book, FALSE);
+		set_live_mode (backend, FALSE);
+}
+
+static void
+proxy_settings_changed (EProxy *proxy, EBookBackend *backend)
+{
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+	SoupURI *proxy_uri = NULL;
+	gchar *uri;
+
+	if (!priv || !priv->service)
+		return;
+
+	/* Build the URI which libgdata would use to query contacts */
+	uri = g_strconcat (priv->use_ssl ? "https" : "http", URI_GET_CONTACTS, NULL);
+
+	/* use proxy if necessary */
+	if (e_proxy_require_proxy_for_uri (proxy, uri))
+		proxy_uri = e_proxy_peek_uri_for (proxy, uri);
+	gdata_service_set_proxy_uri (GDATA_SERVICE (priv->service), proxy_uri);
+
+	g_free (uri);
 }
 
 static EBookBackendSyncStatus
 e_book_backend_google_authenticate_user (EBookBackendSync *backend, EDataBook *book, guint32 opid,
                                          const gchar *username, const gchar *password, const gchar *auth_method)
 {
-	EBookBackendGooglePrivate *priv;
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_Success;
 	GError *error = NULL;
-	gchar *book_username;
 	gboolean match;
 
 	__debug__ (G_STRFUNC);
-	priv = GET_PRIVATE (backend);
 
 	if (priv->mode != GNOME_Evolution_Addressbook_MODE_REMOTE)
 		return GNOME_Evolution_Addressbook_Success;
 
-	if (!username || username[0] == 0)
-		return GNOME_Evolution_Addressbook_AuthenticationFailed;
+	if (priv->service) {
+		g_warning ("Connection to Google already established.");
+		e_book_backend_notify_writable (E_BOOK_BACKEND (backend), TRUE);
+		return GNOME_Evolution_Addressbook_Success;
+	}
 
-	g_object_get (G_OBJECT (priv->book), "username", &book_username, NULL);
+	if (!username || username[0] == 0 || !password || password[0] == 0)
+		return GNOME_Evolution_Addressbook_AuthenticationFailed;
 
-	match = (0 == strcmp (username, book_username));
-	g_free (book_username);
-	if (FALSE == match) {
+	match = (strcmp (username, priv->username) == 0);
+	if (!match) {
 		g_warning ("Username given when loading source and on authentication did not match!");
 		return GNOME_Evolution_Addressbook_AuthenticationRequired;
 	}
 
-	google_book_connect_to_google (priv->book, password, &error);
+	/* Set up the service and proxy */
+	priv->service = GDATA_SERVICE (gdata_contacts_service_new ("evolution-client-0.1.0"));
+
+	priv->proxy = e_proxy_new ();
+	e_proxy_setup_proxy (priv->proxy);
+
+	proxy_settings_changed (priv->proxy, E_BOOK_BACKEND (backend));
+	g_signal_connect (priv->proxy, "changed", G_CALLBACK (proxy_settings_changed), backend);
+
+	/* Authenticate with the server */
+	if (!gdata_service_authenticate (priv->service, priv->username, password, NULL, &error)) {
+		g_object_unref (priv->service);
+		priv->service = NULL;
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+
+		status = e_book_backend_status_from_gdata_error (error);
+		__debug__ ("Authentication failed: %s", error->message);
+		g_error_free (error);
+
+		return status;
+	}
+
+	/* Update the cache if neccessary */
+	cache_refresh_if_needed (E_BOOK_BACKEND (backend), &error);
+
 	if (error) {
-		status = e_book_backend_status_from_google_book_error (error);
+		status = e_book_backend_status_from_gdata_error (error);
 		__debug__ ("Authentication failed: %s", error->message);
-		g_clear_error (&error);
-	} else {
-		e_book_backend_notify_writable (E_BOOK_BACKEND (backend), TRUE);
+		g_error_free (error);
+
+		return status;
 	}
 
-	return status;
+	e_book_backend_notify_writable (E_BOOK_BACKEND (backend), TRUE);
+
+	return GNOME_Evolution_Addressbook_Success;
 }
 
 static EBookBackendSyncStatus
 e_book_backend_google_get_supported_auth_methods (EBookBackendSync *backend, EDataBook *book, guint32 opid, GList **methods)
 {
-	gchar *auth_method;
-
 	__debug__ (G_STRFUNC);
-	auth_method = g_strdup_printf ("plain/password");
-	*methods = g_list_append (NULL, auth_method);
 
+	*methods = g_list_prepend (NULL, g_strdup ("plain/password"));
 	return GNOME_Evolution_Addressbook_Success;
 }
 
@@ -476,9 +1170,10 @@ e_book_backend_google_get_supported_fields (EBookBackendSync *backend, EDataBook
 
 	__debug__ (G_STRFUNC);
 
+	/* Add all the fields above to the list */
 	for (i = 0; i < G_N_ELEMENTS (supported_fields); i++) {
 		const gchar *field_name = e_contact_field_name (supported_fields[i]);
-		fields = g_list_append (fields, g_strdup (field_name));
+		fields = g_list_prepend (fields, g_strdup (field_name));
 	}
 
 	*fields_out = fields;
@@ -500,26 +1195,47 @@ e_book_backend_google_remove (EBookBackendSync *backend, EDataBook *book, guint3
 }
 
 static void
-on_google_book_auth_required (GoogleBook *book, gpointer user_data)
+set_offline_mode (EBookBackend *backend, gboolean offline)
 {
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
 	__debug__ (G_STRFUNC);
-	e_book_backend_notify_auth_required (E_BOOK_BACKEND (user_data));
+
+	priv->offline = offline;
+
+	if (offline) {
+		/* Going offline, so we can free our service and proxy */
+		if (priv->service)
+			g_object_unref (priv->service);
+		priv->service = NULL;
+
+		if (priv->proxy)
+			g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	} else {
+		/* Going online, so we need to re-authenticate and re-create the service and proxy.
+		 * This is done in e_book_backend_google_authenticate_user() when it gets the authentication data. */
+		e_book_backend_notify_auth_required (backend);
+	}
 }
 
 static GNOME_Evolution_Addressbook_CallStatus
 e_book_backend_google_load_source (EBookBackend *backend, ESource *source, gboolean only_if_exists)
 {
-	EBookBackendGooglePrivate *priv = GET_PRIVATE (backend);
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	const gchar *refresh_interval_str, *use_ssl_str, *use_cache_str;
 	guint refresh_interval;
 	gboolean use_ssl, use_cache;
 	const gchar *username;
 
-	if (priv->book) {
+	__debug__ (G_STRFUNC);
+
+	if (priv->username) {
 		g_warning ("Source already loaded!");
 		return GNOME_Evolution_Addressbook_OtherError;
 	}
 
+	/* Parse the username property */
 	username = e_source_get_property (source, "username");
 
 	if (!username || username[0] == '\0') {
@@ -527,6 +1243,7 @@ e_book_backend_google_load_source (EBookBackend *backend, ESource *source, gbool
 		return GNOME_Evolution_Addressbook_OtherError;
 	}
 
+	/* Parse various other properties */
 	refresh_interval_str = e_source_get_property (source, "refresh-interval");
 	use_ssl_str = e_source_get_property (source, "ssl");
 	use_cache_str = e_source_get_property (source, "offline_sync");
@@ -544,26 +1261,23 @@ e_book_backend_google_load_source (EBookBackend *backend, ESource *source, gbool
 	if (use_cache_str && (g_ascii_strcasecmp (use_cache_str, "false") == 0 || strcmp (use_cache_str, "0") == 0))
 		use_cache = FALSE;
 
-	priv->book = google_book_new (username, use_cache);
-
-	g_object_set (G_OBJECT (priv->book),
-	              "refresh-interval", refresh_interval,
-	              "use-ssl", use_ssl,
-	              NULL);
-	g_object_connect (G_OBJECT (priv->book),
-	                  "signal::contact-added", G_CALLBACK (on_google_book_contact_added), backend,
-	                  "signal::contact-changed", G_CALLBACK (on_google_book_contact_changed), backend,
-	                  "signal::contact-removed", G_CALLBACK (on_google_book_contact_removed), backend,
-	                  "signal::sequence-complete", G_CALLBACK (on_google_book_sequence_complete), backend,
-	                  "signal::auth-required", G_CALLBACK (on_google_book_auth_required), backend,
-	                  NULL);
+	/* Set up our object */
+	priv->username = g_strdup (username);
+	cache_init (backend, use_cache);
+	priv->use_ssl = use_ssl;
+	priv->refresh_interval = refresh_interval;
 
-	__debug__ (G_STRFUNC);
+	/* Remove and re-add the timeout */
+	if (priv->refresh_id != 0) {
+		g_source_remove (priv->refresh_id);
+		priv->refresh_id = g_timeout_add_seconds (priv->refresh_interval, (GSourceFunc) on_refresh_timeout, backend);
+	}
 
+	/* Set up ready to be interacted with */
 	e_book_backend_set_is_loaded (backend, TRUE);
 	e_book_backend_notify_connection_status (backend, TRUE);
 	e_book_backend_set_is_writable (backend, FALSE);
-	google_book_set_offline_mode (priv->book, (priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL));
+	set_offline_mode (backend, (priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL));
 
 	return GNOME_Evolution_Addressbook_Success;
 }
@@ -572,7 +1286,7 @@ static gchar *
 e_book_backend_google_get_static_capabilities (EBookBackend *backend)
 {
 	__debug__ (G_STRFUNC);
-	return g_strdup("net,do-initial-query,contact-lists");
+	return g_strdup ("net,do-initial-query,contact-lists");
 }
 
 static GNOME_Evolution_Addressbook_CallStatus
@@ -585,7 +1299,7 @@ e_book_backend_google_cancel_operation (EBookBackend *backend, EDataBook *book)
 static void
 e_book_backend_google_set_mode (EBookBackend *backend, GNOME_Evolution_Addressbook_BookMode mode)
 {
-	EBookBackendGooglePrivate *priv = GET_PRIVATE (backend);
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
 	gboolean online = (mode == GNOME_Evolution_Addressbook_MODE_REMOTE);
 
 	__debug__ (G_STRFUNC);
@@ -595,8 +1309,7 @@ e_book_backend_google_set_mode (EBookBackend *backend, GNOME_Evolution_Addressbo
 
 	priv->mode = mode;
 
-	if (priv->book)
-		google_book_set_offline_mode (priv->book, !online);
+	set_offline_mode (backend, !online);
 	e_book_backend_notify_connection_status (backend, online);
 
 	/* Mark the book as unwriteable if we're going offline, but don't do the inverse when we go online;
@@ -608,7 +1321,7 @@ e_book_backend_google_set_mode (EBookBackend *backend, GNOME_Evolution_Addressbo
 static void
 e_book_backend_google_dispose (GObject *object)
 {
-	EBookBackendGooglePrivate *priv = GET_PRIVATE (object);
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (object)->priv;
 
 	__debug__ (G_STRFUNC);
 
@@ -617,19 +1330,33 @@ e_book_backend_google_dispose (GObject *object)
 		priv->bookviews = g_list_delete_link (priv->bookviews, priv->bookviews);
 	}
 
-	if (priv->book) {
-		g_object_unref (priv->book);
-		priv->book = NULL;
+	if (priv->idle_id) {
+		g_source_remove (priv->idle_id);
+		priv->idle_id = 0;
 	}
 
+	if (priv->service)
+		g_object_unref (priv->service);
+	priv->service = NULL;
+
+	if (priv->proxy)
+		g_object_unref (priv->proxy);
+	priv->proxy = NULL;
+
+	cache_destroy (E_BOOK_BACKEND (object));
+
 	G_OBJECT_CLASS (e_book_backend_google_parent_class)->dispose (object);
 }
 
 static void
 e_book_backend_google_finalize (GObject *object)
 {
+	EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (object)->priv;
+
 	__debug__ (G_STRFUNC);
 
+	g_free (priv->username);
+
 	G_OBJECT_CLASS (e_book_backend_google_parent_class)->finalize (object);
 }
 
@@ -673,6 +1400,7 @@ static void
 e_book_backend_google_init (EBookBackendGoogle *backend)
 {
 	__debug__ (G_STRFUNC);
+	backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, E_TYPE_BOOK_BACKEND_GOOGLE, EBookBackendGooglePrivate);
 }
 
 EBookBackend *
@@ -687,7 +1415,7 @@ e_book_backend_google_new (void)
 }
 
 static EBookBackendSyncStatus
-e_book_backend_status_from_google_book_error (GError *error)
+e_book_backend_status_from_gdata_error (GError *error)
 {
 	if (!error)
 		return GNOME_Evolution_Addressbook_Success;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 360c5aa..b97c86b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -3,7 +3,6 @@
 [encoding: UTF-8]
 addressbook/backends/file/e-book-backend-file.c
 addressbook/backends/google/e-book-backend-google.c
-addressbook/backends/google/google-book.c
 addressbook/backends/google/util.c
 addressbook/backends/groupwise/e-book-backend-groupwise.c
 addressbook/backends/ldap/e-book-backend-ldap.c



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