evolution-data-server r9151 - in trunk: . addressbook addressbook/backends addressbook/backends/google



Author: abharath
Date: Mon Jul 21 03:57:49 2008
New Revision: 9151
URL: http://svn.gnome.org/viewvc/evolution-data-server?rev=9151&view=rev

Log:
Committing on behalf of JÃrgen Scheibengruber  <mfcn gmx de>

2008-07-21  JÃrgen Scheibengruber  <mfcn gmx de>

        ** Fix for bug #523632

        Added Google contacts backend


Added:
   trunk/addressbook/backends/google/
   trunk/addressbook/backends/google/Makefile.am
   trunk/addressbook/backends/google/e-book-backend-google-factory.c
   trunk/addressbook/backends/google/e-book-backend-google-factory.h
   trunk/addressbook/backends/google/e-book-backend-google.c
   trunk/addressbook/backends/google/e-book-backend-google.h
   trunk/addressbook/backends/google/util.c
   trunk/addressbook/backends/google/util.h
Modified:
   trunk/ChangeLog
   trunk/addressbook/ChangeLog
   trunk/addressbook/backends/Makefile.am
   trunk/configure.in

Modified: trunk/addressbook/backends/Makefile.am
==============================================================================
--- trunk/addressbook/backends/Makefile.am	(original)
+++ trunk/addressbook/backends/Makefile.am	Mon Jul 21 03:57:49 2008
@@ -4,4 +4,4 @@
 LDAP_SUBDIR =
 endif
 
-SUBDIRS = file vcf $(LDAP_SUBDIR) groupwise 
+SUBDIRS = file vcf $(LDAP_SUBDIR) google groupwise 

Added: trunk/addressbook/backends/google/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/addressbook/backends/google/Makefile.am	Mon Jul 21 03:57:49 2008
@@ -0,0 +1,32 @@
+INCLUDES = \
+	-DG_LOG_DOMAIN=\"libebookbackendgoogle\" \
+	-I$(top_srcdir)/addressbook \
+	-I$(top_builddir)/addressbook \
+	-I$(top_srcdir)/servers/google/libgdata \
+	-I$(top_builddir)/servers/google/libgdata \
+	-I$(top_srcdir)/servers/google/libgdata-google \
+	-I$(top_builddir)/servers/google/libgdata-google \
+	$(SOUP_CFLAGS) \
+	$(EVOLUTION_ADDRESSBOOK_CFLAGS)
+
+extension_LTLIBRARIES = libebookbackendgoogle.la
+
+libebookbackendgoogle_la_SOURCES = \
+	util.h \
+	util.c \
+	e-book-backend-google-factory.h \
+	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)/servers/google/libgdata/libgdata-1.2.la \
+	$(top_builddir)/servers/google/libgdata-google/libgdata-google-1.2.la \
+	$(SOUP_LIBS) \
+	$(EVOLUTION_ADDRESSBOOK_LIBS)
+
+libebookbackendgoogle_la_LDFLAGS = \
+	-module -avoid-version $(NO_UNDEFINED)

Added: trunk/addressbook/backends/google/e-book-backend-google-factory.c
==============================================================================
--- (empty file)
+++ trunk/addressbook/backends/google/e-book-backend-google-factory.c	Mon Jul 21 03:57:49 2008
@@ -0,0 +1,95 @@
+/* e-book-backend-google-factory.c - Google contact backend factory.
+ *
+ * Copyright (C) 2008 Joergen Scheibengruber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Joergen Scheibengruber <joergen.scheibengruber AT googlemail.com>
+ */
+
+#include <config.h>
+
+#include <libedataserver/e-data-server-module.h>
+#include <libedata-book/e-book-backend-factory.h>
+#include "e-book-backend-google-factory.h"
+#include "e-book-backend-google.h"
+
+static GType google_type;
+
+static void
+e_book_backend_google_factory_instance_init (EBookBackendGoogleFactory *factory)
+{
+}
+
+static const char *
+_get_protocol (EBookBackendFactory *factory)
+{
+    return "google";
+}
+
+static EBookBackend*
+_new_backend (EBookBackendFactory *factory)
+{
+    return e_book_backend_google_new ();
+}
+
+static void
+e_book_backend_google_factory_class_init (EBookBackendGoogleFactoryClass *klass)
+{
+  E_BOOK_BACKEND_FACTORY_CLASS (klass)->get_protocol = _get_protocol;
+  E_BOOK_BACKEND_FACTORY_CLASS (klass)->new_backend = _new_backend;
+}
+
+GType
+e_book_backend_google_factory_get_type (GTypeModule *module)
+{
+    static GType  type = 0;
+
+    if (!type) {
+        GTypeInfo info = {
+            sizeof (EBookBackendGoogleFactoryClass),
+            NULL, /* base_class_init */
+            NULL, /* base_class_finalize */
+            (GClassInitFunc)  e_book_backend_google_factory_class_init,
+            NULL, /* class_finalize */
+            NULL, /* class_data */
+            sizeof (EBookBackend),
+            0,    /* n_preallocs */
+            (GInstanceInitFunc) e_book_backend_google_factory_instance_init
+        };
+
+        type = g_type_module_register_type (module, E_TYPE_BOOK_BACKEND_FACTORY,
+                                            "EBookBackendGoogleFactory", &info, 0);
+    }
+    return type;
+}
+
+void
+eds_module_initialize (GTypeModule *module)
+{
+    google_type = e_book_backend_google_factory_get_type (module);
+}
+
+void
+eds_module_shutdown   (void)
+{
+}
+
+void
+eds_module_list_types (const GType **types, int *num_types)
+{
+    *types = &google_type;
+    *num_types = 1;
+}

Added: trunk/addressbook/backends/google/e-book-backend-google-factory.h
==============================================================================
--- (empty file)
+++ trunk/addressbook/backends/google/e-book-backend-google-factory.h	Mon Jul 21 03:57:49 2008
@@ -0,0 +1,53 @@
+/* e-book-backend-google-factory.h - Google contact backend factory.
+ *
+ * Copyright (C) 2008 Joergen Scheibengruber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Joergen Scheibengruber <joergen.scheibengruber AT googlemail.com>
+ */
+
+#ifndef __E_BOOK_BACKEND_GOOGLE_FACTORY_H__
+#define __E_BOOK_BACKEND_GOOGLE_FACTORY_H__
+
+#include <glib-object.h>
+#include <libedata-book/e-book-backend-factory.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_BOOK_BACKEND_GOOGLE_FACTORY         (e_book_backend_exchange_factory_get_type ())
+#define E_BOOK_BACKEND_GOOGLE_FACTORY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_BOOK_BACKEND_GOOGLE_FACTORY, EBookBackendGoogleFactory))
+#define E_BOOK_BACKEND_GOOGLE_FACTORY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), E_TYPE_BOOK_BACKEND_GOOGLE_FACTORY, EBookBackendGoogleFactoryClass))
+#define E_IS_BOOK_BACKEND_GOOGLE_FACTORY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BOOK_BACKEND_GOOGLE_FACTORY))
+#define E_IS_BOOK_BACKEND_GOOGLE_FACTORY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BOOK_BACKEND_GOOGLE_FACTORY))
+#define E_BOOK_BACKEND_GOOGLE_FACTORY_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_BOOK_BACKEND_GOOGLE_FACTORY, EBookBackendGoogleFactoryClass))
+
+typedef struct _EBookBackendGoogleFactory      EBookBackendGoogleFactory;
+typedef struct _EBookBackendGoogleFactoryClass EBookBackendGoogleFactoryClass;
+
+struct _EBookBackendGoogleFactory {
+    EBookBackendFactory      parent_object;
+};
+
+struct _EBookBackendGoogleFactoryClass{
+    EBookBackendFactoryClass parent_class;
+};
+
+GType e_book_backend_google_factory_get_type (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __E_BOOK_BACKEND_GOOGLE_FACTORY_H__ */
+ 

Added: trunk/addressbook/backends/google/e-book-backend-google.c
==============================================================================
--- (empty file)
+++ trunk/addressbook/backends/google/e-book-backend-google.c	Mon Jul 21 03:57:49 2008
@@ -0,0 +1,992 @@
+/* e-book-backend-google.c - Google contact backendy.
+ *
+ * Copyright (C) 2008 Joergen Scheibengruber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Joergen Scheibengruber <joergen.scheibengruber AT googlemail.com>
+ */
+
+#include <config.h>
+#include <string.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 <gdata-service-iface.h>
+#include <gdata-google-service.h>
+
+#include "e-book-backend-google.h"
+#include "util.h"
+
+G_DEFINE_TYPE (EBookBackendGoogle, e_book_backend_google, E_TYPE_BOOK_BACKEND_SYNC);
+
+struct _EBookBackendGooglePrivate
+{
+    gint mode;
+    char *base_uri;
+
+    GDataGoogleService *service;
+    GHashTable *gdata_entries;
+
+    guint refresh_interval;
+    guint refresh_feed_id;
+    char *feed_last_updated;
+
+    gboolean authorized;
+
+    GList *bookviews;
+    GList *pending_auth_bookviews;
+};
+
+#define GET_PRIVATE(obj)      \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((obj),  \
+     E_TYPE_BOOK_BACKEND_GOOGLE,  \
+     EBookBackendGooglePrivate))
+
+gboolean __e_book_backend_google_debug__;
+
+static EBookBackendSyncStatus
+e_book_backend_google_initial_query (EBookBackendGoogle *backend);
+
+static EBookBackendSyncStatus
+e_book_backend_google_update_query  (EBookBackendGoogle *backend,
+                                     GList             **added,
+                                     GList             **modified,
+                                     GList             **deleted);
+
+static EBookBackendSyncStatus
+ebookbackend_status_from_soup_error (int http_error);
+
+static gboolean
+e_book_backend_google_ensure_auth (EBookBackendGoogle *backend)
+{
+    EBookBackendGooglePrivate *priv;
+    gboolean is_authorized;
+
+    priv = GET_PRIVATE (backend);
+
+    is_authorized = (priv->authorized && priv->base_uri && priv->service);
+
+    if (FALSE == is_authorized) {
+        e_book_backend_notify_auth_required (E_BOOK_BACKEND (backend));
+        __debug__ ("auth required");
+    }
+
+    return is_authorized;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_create_contact (EBookBackendSync *backend,
+                                      EDataBook        *book,
+                                      guint32           opid,
+                                      const char       *vcard_str,
+                                      EContact        **contact)
+{
+    EBookBackendGooglePrivate *priv;
+    EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_OtherError;
+    GDataEntry *entry, *new_entry;
+    GError *error = NULL;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    __debug__ ("Creating: %s", vcard_str);
+    *contact = NULL;
+
+    if (priv->mode != GNOME_Evolution_Addressbook_MODE_REMOTE) {
+        return GNOME_Evolution_Addressbook_OfflineUnavailable;
+    }
+
+    if (FALSE == e_book_backend_google_ensure_auth (E_BOOK_BACKEND_GOOGLE (backend))) {
+        return GNOME_Evolution_Addressbook_AuthenticationRequired;
+    }
+
+    entry = gdata_entry_create_from_vcard (vcard_str);
+    __debug__ ("new entry with xml: %s", gdata_entry_generate_xml (entry));
+    new_entry = gdata_service_insert_entry (GDATA_SERVICE (priv->service),
+                                            priv->base_uri, entry, &error);
+    g_object_unref (entry);
+
+    if (new_entry) {
+        const char *uid;
+
+        uid = gdata_entry_get_id (new_entry);
+        *contact = e_contact_from_gdata_entry (new_entry);
+        g_hash_table_insert (priv->gdata_entries, g_strdup (uid), new_entry);
+    }
+    if (error) {
+        status = ebookbackend_status_from_soup_error (error->code);
+        __debug__ ("Creating contact failed (HTTP %d): %s", error->code, error->message);
+        g_clear_error (&error);
+    }
+
+    if (NULL == *contact) {
+        return status;
+    }
+    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;
+    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;
+    }
+
+    if (FALSE == e_book_backend_google_ensure_auth (E_BOOK_BACKEND_GOOGLE (backend))) {
+        return GNOME_Evolution_Addressbook_AuthenticationRequired;
+    }
+
+    for (id_iter = id_list; id_iter; id_iter = id_iter->next) {
+        GDataEntry *entry;
+        GError *error = NULL;
+        char *uid;
+
+        uid = id_iter->data;
+        entry = g_hash_table_lookup (priv->gdata_entries, uid);
+        if (NULL == entry) {
+            continue;
+        }
+
+        if (gdata_service_delete_entry (GDATA_SERVICE (priv->service), entry, &error)) {
+            g_hash_table_remove (priv->gdata_entries, uid);
+            *ids = g_list_append (*ids, g_strdup (uid));
+        }
+        if (error) {
+            /* Only last error will be reported */
+            status = ebookbackend_status_from_soup_error (error->code);
+            __debug__ ("Deleting contact %s failed (HTTP %d): %s", uid, error->code, error->message);
+            g_clear_error (&error);
+        }
+    }
+
+    if (NULL == *ids) {
+        return status;
+    }
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_modify_contact (EBookBackendSync *backend,
+                                      EDataBook        *book,
+                                      guint32           opid,
+                                      const char       *vcard_str,
+                                      EContact        **contact)
+{
+    EBookBackendGooglePrivate *priv;
+    EBookBackendSyncStatus status = GNOME_Evolution_Addressbook_OtherError;
+    EVCardAttribute *uid_attr;
+    EVCard *vcard;
+    GDataEntry *entry, *copy, *new_entry;
+    GError *error = NULL;
+    char *uid, *xml;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    __debug__ ("Updating: %s", vcard_str);
+    *contact = NULL;
+
+    if (priv->mode != GNOME_Evolution_Addressbook_MODE_REMOTE) {
+        return GNOME_Evolution_Addressbook_OfflineUnavailable;
+    }
+
+    if (FALSE == e_book_backend_google_ensure_auth (E_BOOK_BACKEND_GOOGLE (backend))) {
+        return GNOME_Evolution_Addressbook_AuthenticationRequired;
+    }
+
+    vcard = e_vcard_new_from_string (vcard_str);
+    if (NULL == vcard)
+        return GNOME_Evolution_Addressbook_OtherError;
+
+    uid_attr = e_vcard_get_attribute (vcard, EVC_UID);
+    if (NULL == uid_attr)
+        return GNOME_Evolution_Addressbook_ContactNotFound;
+
+    uid = e_vcard_attribute_get_value (uid_attr);
+
+    entry = g_hash_table_lookup (priv->gdata_entries, uid ? uid : "");
+
+    if (NULL == entry)
+        return GNOME_Evolution_Addressbook_ContactNotFound;
+
+    xml = gdata_entry_generate_xml (entry);
+    /* We operate on a copy; if we cannot commit our changes we should also
+     * leave the local entry as it was.
+     * There is a warning from libxml2 as result form this, because libgdata
+     * caches the xml from which it was created. However, if it is created
+     * from a feed, this xml will miss all namespace information, thus the warning */
+    copy = gdata_entry_new_from_xml (xml);
+
+    gdata_entry_update_from_e_vcard (copy, vcard);
+    new_entry = gdata_service_update_entry (GDATA_SERVICE (priv->service), copy, &error);
+
+    g_object_unref (copy);
+    g_object_unref (vcard);
+
+    if (new_entry) {
+        *contact = e_contact_from_gdata_entry (new_entry);
+        /* Will unref/free the old entry */
+        g_hash_table_replace (priv->gdata_entries, uid, new_entry);
+    }
+    if (error) {
+        status = ebookbackend_status_from_soup_error (error->code);
+        __debug__ ("Updating contact failed (HTTP %d): %s", error->code, error->message);
+        g_clear_error (&error);
+    }
+
+    if (NULL == *contact) {
+        return status;
+    }
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_get_contact (EBookBackendSync *backend,
+                                   EDataBook        *book,
+                                   guint32           opid,
+                                   const char       *uid,
+                                   char            **vcard_str)
+{
+    EBookBackendGooglePrivate *priv;
+    GDataEntry *entry;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    if (FALSE == e_book_backend_google_ensure_auth (E_BOOK_BACKEND_GOOGLE (backend))) {
+        return GNOME_Evolution_Addressbook_AuthenticationRequired;
+    }
+
+    entry = g_hash_table_lookup (priv->gdata_entries, uid);
+
+    if (NULL == entry) {
+        *vcard_str = NULL;
+        return GNOME_Evolution_Addressbook_ContactNotFound;
+    }
+
+    *vcard_str = vcard_from_gdata_entry (GDATA_ENTRY (entry));
+
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_get_contact_list (EBookBackendSync *backend,
+                                        EDataBook        *book,
+                                        guint32           opid,
+                                        const char       *query,
+                                        GList           **contacts)
+{
+    EBookBackendGooglePrivate *priv;
+    EBookBackendSExp *sexp;
+    GHashTableIter iter;
+    GDataEntry *entry;
+    char *uid;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    *contacts = NULL;
+    if (FALSE == e_book_backend_google_ensure_auth (E_BOOK_BACKEND_GOOGLE (backend))) {
+        return GNOME_Evolution_Addressbook_AuthenticationRequired;
+    }
+
+    sexp = e_book_backend_sexp_new (query);
+
+    g_hash_table_iter_init (&iter, priv->gdata_entries);
+    while (g_hash_table_iter_next (&iter, (gpointer)&uid, (gpointer)&entry)) {
+        char *vcard_str;
+
+        vcard_str = vcard_from_gdata_entry (GDATA_ENTRY (entry));
+        if (vcard_str && e_book_backend_sexp_match_vcard (sexp, vcard_str)) {
+            *contacts = g_list_append (*contacts, vcard_str);
+        } else {
+            g_free (vcard_str);
+        }
+    }
+    g_object_unref (sexp);
+
+
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static gboolean
+refresh_feed (gpointer data)
+{
+    EBookBackendGooglePrivate *priv;
+    GList *added, *modified, *deleted;
+    GList *iter;
+
+    g_return_val_if_fail (E_IS_BOOK_BACKEND_GOOGLE (data), FALSE);
+
+    priv = GET_PRIVATE (data);
+
+    if (NULL == priv->bookviews) {
+        priv->refresh_feed_id = 0;
+
+        return FALSE;
+    }
+    e_book_backend_google_update_query (E_BOOK_BACKEND_GOOGLE (data),
+                                        &added, &modified, &deleted);
+
+    if (FALSE == (added || modified || deleted))
+        return TRUE;
+
+    modified = g_list_concat (added, modified);
+    while (modified) {
+        char *vcard_str;
+
+        vcard_str = vcard_from_gdata_entry (GDATA_ENTRY (modified->data));
+        __debug__ ("Update or new entry: %s", vcard_str);
+        if (vcard_str) {
+            for (iter = priv->bookviews; iter; iter = iter->next) {
+                e_data_book_view_notify_update_vcard (iter->data, g_strdup (vcard_str));
+            }
+            g_free (vcard_str);
+        }
+
+        g_object_unref (modified->data);
+        modified = g_list_delete_link (modified, modified);
+    }
+
+    while (deleted) {
+        const char *uid;
+
+        uid = gdata_entry_get_id (GDATA_ENTRY (deleted->data));
+        __debug__ ("Deleted entry: %s", uid);
+        if (uid) {
+            for (iter = priv->bookviews; iter; iter = iter->next) {
+                e_data_book_view_notify_remove (iter->data, uid);
+            }
+        }
+
+        g_object_unref (deleted->data);
+        deleted = g_list_delete_link (deleted, deleted);
+    }
+
+    for (iter = priv->bookviews; iter; iter = iter->next) {
+        e_data_book_view_notify_complete (iter->data, GNOME_Evolution_Addressbook_Success);
+    }
+
+    return TRUE;
+}
+
+static void
+do_initial_bookview_population (EBookBackendGoogle *backend,
+                                EDataBookView      *bookview)
+{
+    EBookBackendGooglePrivate *priv;
+    GHashTableIter iter;
+    GDataEntry *entry;
+    char *uid;
+
+    priv = GET_PRIVATE (backend);
+
+    if (NULL == priv->gdata_entries)
+        return;
+
+    g_hash_table_iter_init (&iter, priv->gdata_entries);
+    while (g_hash_table_iter_next (&iter, (gpointer)&uid, (gpointer)&entry)) {
+        char *vcard_str;
+
+        vcard_str = vcard_from_gdata_entry (GDATA_ENTRY (entry));
+        //__debug__ ("%s", vcard_str);
+        e_data_book_view_notify_update_vcard (bookview, vcard_str);
+
+    }
+
+    e_data_book_view_notify_complete (bookview, GNOME_Evolution_Addressbook_Success);
+
+    if ((0 == priv->refresh_feed_id) && priv->refresh_interval) {
+        priv->refresh_feed_id = g_timeout_add_seconds (priv->refresh_interval,
+                                                       refresh_feed,
+                                                       backend);
+    }
+}
+
+static void
+e_book_backend_google_start_book_view (EBookBackend  *backend,
+                                       EDataBookView *bookview)
+{
+    EBookBackendGooglePrivate *priv;
+    gboolean have_auth;
+
+    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);
+
+    bonobo_object_ref (bookview);
+    e_data_book_view_notify_status_message (bookview, "Loading...");
+
+    have_auth = e_book_backend_google_ensure_auth (E_BOOK_BACKEND_GOOGLE (backend));
+    if (have_auth) {
+        do_initial_bookview_population (E_BOOK_BACKEND_GOOGLE (backend),
+                                        bookview);
+    } else {
+        priv->pending_auth_bookviews = g_list_append (priv->pending_auth_bookviews,
+                                                      bookview);
+    }
+}
+
+static void
+e_book_backend_google_stop_book_view (EBookBackend  *backend,
+                                      EDataBookView *bookview)
+{
+    EBookBackendGooglePrivate *priv;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    priv->bookviews = g_list_remove (priv->bookviews, bookview);
+    priv->pending_auth_bookviews = g_list_remove (priv->pending_auth_bookviews, bookview);
+    bonobo_object_unref (bookview);
+
+    if ((NULL == priv->bookviews) && priv->refresh_feed_id) {
+        g_source_remove (priv->refresh_feed_id);
+        priv->refresh_feed_id = 0;
+    }
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_update_query  (EBookBackendGoogle *backend,
+                                     GList             **added,
+                                     GList             **modified,
+                                     GList             **deleted)
+{
+    EBookBackendGooglePrivate *priv;
+    GDataFeed *feed;
+    GSList *iter, *entries;
+    GError *error = NULL;
+    char *uri, *updated_min = NULL;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    *added = *modified = *deleted = NULL;
+
+    if (NULL == priv->service ||
+        NULL == priv->base_uri) {
+        return GNOME_Evolution_Addressbook_AuthenticationFailed;
+    }
+
+    if (priv->feed_last_updated) {
+        updated_min = g_strdup_printf ("updated-min=%s", priv->feed_last_updated);
+    }
+
+    uri = build_uri (priv->base_uri, "max-results=100", "showdeleted=true", updated_min,  NULL);
+    __debug__ ("URI is '%s'", uri);
+    feed = gdata_service_get_feed (GDATA_SERVICE (priv->service), uri, &error);
+    g_free (uri);
+
+    if (error) {
+        EBookBackendSyncStatus status;
+
+        __debug__ ("Update query failed (HTTP %d): %s", error->code, error->message);
+        status = ebookbackend_status_from_soup_error (error->code);
+        g_clear_error (&error);
+
+        return status;
+    }
+    if (NULL == feed) {
+        return GNOME_Evolution_Addressbook_OtherError;
+    }
+
+    g_free (priv->feed_last_updated);
+    priv->feed_last_updated = g_strdup (gdata_feed_get_updated (GDATA_FEED (feed)));
+
+    entries = gdata_feed_get_entries (feed);
+    __debug__ ("Update-feed has %d entries", entries ? g_slist_length (entries) : -1);
+
+    for (iter = entries; iter; iter = iter->next) {
+        GDataEntry *entry, *old_entry;
+        const char *edit_link, *old_edit_link;
+        gboolean is_deleted;
+        const char *uid;
+
+        entry = GDATA_ENTRY (iter->data);
+        uid = gdata_entry_get_id (entry);
+        is_deleted = gdata_entry_is_deleted (entry);
+
+        old_entry = g_hash_table_lookup (priv->gdata_entries, uid);
+        if (is_deleted) {
+            /* Do we have this item in our list? */
+            if (NULL == old_entry) {
+                continue;
+            } else {
+                *deleted = g_list_append (*deleted, g_object_ref (entry));
+                g_hash_table_remove (priv->gdata_entries, uid);
+            }
+        } else {
+            /* Is this a new entry */
+            if (NULL == old_entry) {
+                *added = g_list_append (*added, g_object_ref (entry));
+                g_hash_table_insert (priv->gdata_entries,
+                                     g_strdup (uid),
+                                     g_object_ref (entry));
+            }
+        }
+
+        edit_link = gdata_entry_get_edit_link (entry);
+        old_edit_link = gdata_entry_get_edit_link (old_entry);
+        if (0 == strcmp (edit_link ? edit_link : "", old_edit_link ? old_edit_link : ""))
+            continue;
+
+        g_hash_table_replace (priv->gdata_entries, g_strdup (uid), g_object_ref (entry));
+        *modified = g_list_append (*modified, g_object_ref (entry));
+    }
+    g_object_unref (feed);
+
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_initial_query (EBookBackendGoogle *backend)
+{
+    EBookBackendGooglePrivate *priv;
+    GDataFeed *feed;
+    GSList *iter, *entries;
+    GError *error = NULL;
+    char *uri;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    if (NULL == priv->service ||
+        NULL == priv->base_uri) {
+        return GNOME_Evolution_Addressbook_AuthenticationFailed;
+    }
+
+    uri = build_uri (priv->base_uri, "max-results=100", NULL);
+    __debug__ ("URI is '%s'", uri);
+    feed = gdata_service_get_feed (GDATA_SERVICE (priv->service), uri, &error);
+    g_free (uri);
+
+    if (error) {
+        EBookBackendSyncStatus status;
+
+        __debug__ ("Initial query failed (HTTP %d): %s", error->code, error->message);
+        status = ebookbackend_status_from_soup_error (error->code);
+        g_clear_error (&error);
+
+        return status;
+    }
+    if (NULL == feed) {
+        return GNOME_Evolution_Addressbook_OtherError;
+    }
+
+    g_free (priv->feed_last_updated);
+    priv->feed_last_updated = g_strdup (gdata_feed_get_updated (GDATA_FEED (feed)));
+    if (priv->gdata_entries) {
+        g_hash_table_destroy (priv->gdata_entries);
+    }
+    priv->gdata_entries = g_hash_table_new_full (g_str_hash,
+                                                 g_str_equal,
+                                                 g_free,
+                                                 g_object_unref);
+
+    entries = gdata_feed_get_entries (feed);
+    __debug__ ("Feed has %d entries", entries ? g_slist_length (entries) : -1);
+    for (iter = entries; iter; iter = iter->next) {
+        GDataEntry *entry;
+        const char* uid;
+
+        entry = GDATA_ENTRY (iter->data);
+        uid = gdata_entry_get_id (entry);
+        g_hash_table_insert (priv->gdata_entries, g_strdup (uid), g_object_ref (entry));
+    }
+    g_object_unref (feed);
+
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+
+static EBookBackendSyncStatus
+e_book_backend_google_authenticate_user (EBookBackendSync *backend,
+                                         EDataBook        *book,
+                                         guint32           opid,
+                                         const char       *username,
+                                         const char       *password,
+                                         const char       *auth_method)
+{
+    EBookBackendGooglePrivate *priv;
+    EBookBackendSyncStatus status;
+
+    __debug__ (G_STRFUNC);
+    priv = GET_PRIVATE (backend);
+
+    if (priv->mode != GNOME_Evolution_Addressbook_MODE_REMOTE) {
+        g_warning ("Offline mode not mplemented...");
+        return GNOME_Evolution_Addressbook_OfflineUnavailable;
+    }
+    if (NULL == priv->service) {
+        g_warning ("Book not open; could not authenticate");
+        return GNOME_Evolution_Addressbook_RepositoryOffline;
+    }
+
+    g_free (priv->base_uri);
+    priv->base_uri = NULL;
+
+    if (NULL == username || username[0] == 0 ||
+        NULL == password || password[0] == 0) {
+        return GNOME_Evolution_Addressbook_AuthenticationFailed;
+    }
+
+    priv->base_uri = build_base_uri (username);
+    gdata_service_set_credentials (GDATA_SERVICE (priv->service), username, password);
+
+    status = e_book_backend_google_initial_query (E_BOOK_BACKEND_GOOGLE (backend));
+    if (status == GNOME_Evolution_Addressbook_Success) {
+        GList *iter;
+
+        priv->authorized = TRUE;
+        e_book_backend_notify_writable (E_BOOK_BACKEND (backend), TRUE);
+
+        for (iter = priv->pending_auth_bookviews; iter; iter = iter->next) {
+            do_initial_bookview_population (E_BOOK_BACKEND_GOOGLE (backend),
+                                            iter->data);
+        }
+    } else {
+        g_free (priv->base_uri);
+        priv->base_uri = NULL;
+        priv->authorized = FALSE;
+    }
+
+    return status;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_get_supported_auth_methods (EBookBackendSync *backend,
+                                                  EDataBook        *book,
+                                                  guint32           opid,
+                                                  GList           **methods)
+{
+    char *auth_method;
+
+    __debug__ (G_STRFUNC);
+    auth_method = g_strdup_printf ("plain/password");
+    *methods = g_list_append (NULL, auth_method);
+
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_get_required_fields (EBookBackendSync *backend,
+                                           EDataBook        *book,
+                                           guint32           opid,
+                                           GList           **fields_out)
+{
+    __debug__ (G_STRFUNC);
+
+    *fields_out = NULL;
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_get_supported_fields (EBookBackendSync *backend,
+                                            EDataBook        *book,
+                                            guint32           opid,
+                                            GList           **fields_out)
+{
+    const int supported_fields[] =
+    {
+        E_CONTACT_FULL_NAME,
+        E_CONTACT_EMAIL_1,
+        E_CONTACT_EMAIL_2,
+        E_CONTACT_EMAIL_3,
+        E_CONTACT_EMAIL_4,
+        E_CONTACT_ADDRESS_LABEL_HOME,
+        E_CONTACT_ADDRESS_LABEL_WORK,
+        E_CONTACT_ADDRESS_LABEL_OTHER,
+        E_CONTACT_PHONE_HOME,
+        E_CONTACT_PHONE_HOME_FAX,
+        E_CONTACT_PHONE_BUSINESS,
+        E_CONTACT_PHONE_BUSINESS_FAX,
+        E_CONTACT_PHONE_MOBILE,
+        E_CONTACT_PHONE_PAGER,
+        E_CONTACT_IM_AIM,
+        E_CONTACT_IM_JABBER,
+        E_CONTACT_IM_YAHOO,
+        E_CONTACT_IM_MSN,
+        E_CONTACT_IM_ICQ,
+        E_CONTACT_ADDRESS,
+        E_CONTACT_ADDRESS_HOME,
+        E_CONTACT_ADDRESS_WORK,
+        E_CONTACT_ADDRESS_OTHER
+    };
+    GList *fields = NULL;
+    int i;
+
+    __debug__ (G_STRFUNC);
+
+    for (i = 0; i < G_N_ELEMENTS (supported_fields); i++) {
+        const char *field_name;
+
+        field_name = e_contact_field_name (supported_fields[i]);
+        fields = g_list_append (fields, g_strdup (field_name));
+    }
+
+    *fields_out = fields;
+    return GNOME_Evolution_Addressbook_Success;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_get_changes (EBookBackendSync *backend,
+                                   EDataBook        *book,
+                                   guint32           opid,
+                                   const char       *change_id,
+                                   GList           **changes_out)
+{
+    __debug__ (G_STRFUNC);
+    return GNOME_Evolution_Addressbook_OtherError;
+}
+
+static EBookBackendSyncStatus
+e_book_backend_google_remove (EBookBackendSync *backend,
+                              EDataBook        *book,
+                              guint32           opid)
+{
+    __debug__ (G_STRFUNC);
+    return GNOME_Evolution_Addressbook_PermissionDenied;
+}
+
+static GNOME_Evolution_Addressbook_CallStatus
+e_book_backend_google_load_source (EBookBackend *backend,
+                                   ESource      *source,
+                                   gboolean      only_if_exists)
+{
+    EBookBackendGooglePrivate *priv = GET_PRIVATE (backend);
+    const char *refresh_interval;
+
+    if (priv->service) {
+        g_warning ("Source already loaded!");
+        return GNOME_Evolution_Addressbook_OtherError;
+    }
+
+    refresh_interval = e_source_get_property (source, "refresh-interval");
+    if (refresh_interval) {
+        guint val;
+
+        if (1 == sscanf (refresh_interval, "%u", &val)) {
+            priv->refresh_interval = val;
+        }
+    }
+
+    __debug__ (G_STRFUNC);
+    if (priv->mode == GNOME_Evolution_Addressbook_MODE_REMOTE) {
+        gboolean status;
+
+        status = test_repository_availability ();
+        if (FALSE == status) {
+            e_book_backend_notify_connection_status (backend, FALSE);
+
+            return GNOME_Evolution_Addressbook_RepositoryOffline;
+        }
+
+        priv->service = gdata_google_service_new ("cp", "evolution-client-0.0.1");
+
+        e_book_backend_set_is_loaded (backend, TRUE);
+        e_book_backend_notify_connection_status (backend, TRUE);
+
+        return GNOME_Evolution_Addressbook_Success;
+    } else {
+        g_warning ("Offline mode not yet implemented...");
+        return GNOME_Evolution_Addressbook_OfflineUnavailable;
+    }
+}
+
+static char *
+e_book_backend_google_get_static_capabilities (EBookBackend *backend)
+{
+    __debug__ (G_STRFUNC);
+    return g_strdup("net,do-initial-query,contact-lists");
+}
+
+static GNOME_Evolution_Addressbook_CallStatus
+e_book_backend_google_cancel_operation (EBookBackend *backend, EDataBook *book)
+{
+    __debug__ (G_STRFUNC);
+    return GNOME_Evolution_Addressbook_CouldNotCancel;
+}
+
+static void
+e_book_backend_google_set_mode (EBookBackend *backend, GNOME_Evolution_Addressbook_BookMode mode)
+{
+    EBookBackendGooglePrivate *priv = GET_PRIVATE (backend);
+
+    __debug__ (G_STRFUNC);
+
+    if (mode == priv->mode) {
+        return;
+    }
+    priv->mode = mode;
+
+    if (mode == GNOME_Evolution_Addressbook_MODE_REMOTE) {
+        if (e_book_backend_is_loaded (backend)) {
+            gboolean status;
+
+            status = test_repository_availability ();
+
+            if (FALSE == status) {
+                e_book_backend_notify_writable (backend, FALSE);
+                e_book_backend_notify_connection_status (backend, FALSE);
+            } else {
+                status = e_book_backend_google_ensure_auth (E_BOOK_BACKEND_GOOGLE (backend));
+                e_book_backend_notify_writable (backend, status);
+                e_book_backend_notify_connection_status (backend, TRUE);
+            }
+        } else {
+            e_book_backend_set_is_writable (backend, FALSE);
+        }
+    } else {
+        e_book_backend_notify_writable (backend, FALSE);
+        e_book_backend_notify_connection_status (backend, FALSE);
+        g_warning ("Offline mode not implemented yet...");
+    }
+}
+
+static void
+e_book_backend_google_dispose (GObject *object)
+{
+    EBookBackendGooglePrivate *priv = GET_PRIVATE (object);
+
+    __debug__ (G_STRFUNC);
+
+    if (priv->refresh_feed_id) {
+        g_source_remove (priv->refresh_feed_id);
+        priv->refresh_feed_id = 0;
+    }
+
+    while (priv->pending_auth_bookviews) {
+        priv->pending_auth_bookviews =
+                g_list_delete_link (priv->pending_auth_bookviews,
+                                    priv->pending_auth_bookviews);
+    }
+    while (priv->bookviews) {
+        bonobo_object_unref (priv->bookviews->data);
+        priv->bookviews = g_list_delete_link (priv->bookviews,
+                                              priv->bookviews);
+    }
+    if (priv->service) {
+        g_object_unref (priv->service);
+        priv->service = NULL;
+    }
+
+    G_OBJECT_CLASS (e_book_backend_google_parent_class)->dispose (object);
+}
+
+static void
+e_book_backend_google_finalize (GObject *object)
+{
+    EBookBackendGooglePrivate *priv = GET_PRIVATE (object);
+
+    __debug__ (G_STRFUNC);
+
+    g_free (priv->base_uri);
+    g_free (priv->feed_last_updated);
+    if (priv->gdata_entries) {
+        g_hash_table_destroy (priv->gdata_entries);
+    }
+    G_OBJECT_CLASS (e_book_backend_google_parent_class)->finalize (object);
+}
+
+static void
+e_book_backend_google_class_init (EBookBackendGoogleClass *klass)
+{
+    GObjectClass      *object_class = G_OBJECT_CLASS (klass);
+    EBookBackendClass *backend_class;
+    EBookBackendSyncClass *sync_class;
+
+    backend_class = E_BOOK_BACKEND_CLASS (klass);
+    sync_class = E_BOOK_BACKEND_SYNC_CLASS (klass);
+    g_type_class_add_private (klass, sizeof (EBookBackendGooglePrivate));
+
+    /* Set the virtual methods. */
+    backend_class->load_source             = e_book_backend_google_load_source;
+    backend_class->get_static_capabilities = e_book_backend_google_get_static_capabilities;
+    backend_class->start_book_view         = e_book_backend_google_start_book_view;
+    backend_class->stop_book_view          = e_book_backend_google_stop_book_view;
+    backend_class->cancel_operation        = e_book_backend_google_cancel_operation;
+    backend_class->set_mode                = e_book_backend_google_set_mode;
+    sync_class->remove_sync                = e_book_backend_google_remove;
+    sync_class->create_contact_sync        = e_book_backend_google_create_contact;
+    sync_class->remove_contacts_sync       = e_book_backend_google_remove_contacts;
+    sync_class->modify_contact_sync        = e_book_backend_google_modify_contact;
+    sync_class->get_contact_sync           = e_book_backend_google_get_contact;
+    sync_class->get_contact_list_sync      = e_book_backend_google_get_contact_list;
+    sync_class->get_changes_sync           = e_book_backend_google_get_changes;
+    sync_class->authenticate_user_sync     = e_book_backend_google_authenticate_user;
+    sync_class->get_supported_fields_sync  = e_book_backend_google_get_supported_fields;
+    sync_class->get_required_fields_sync   = e_book_backend_google_get_required_fields;
+    sync_class->get_supported_auth_methods_sync  = e_book_backend_google_get_supported_auth_methods;
+
+    object_class->dispose  = e_book_backend_google_dispose;
+    object_class->finalize = e_book_backend_google_finalize;
+
+    __e_book_backend_google_debug__ = g_getenv ("GOOGLE_BACKEND_DEBUG") ? TRUE : FALSE;
+}
+
+static void
+e_book_backend_google_init (EBookBackendGoogle *backend)
+{
+    __debug__ (G_STRFUNC);
+}
+
+EBookBackend *
+e_book_backend_google_new (void)
+{
+    EBookBackendGoogle *backend;
+
+    __debug__ (G_STRFUNC);
+    backend = g_object_new (E_TYPE_BOOK_BACKEND_GOOGLE, NULL);
+
+    return E_BOOK_BACKEND (backend);
+}
+
+static EBookBackendSyncStatus
+ebookbackend_status_from_soup_error (int http_error)
+{
+    if (http_error < 200) {
+        return GNOME_Evolution_Addressbook_RepositoryOffline;
+    } else
+    if (http_error == 401) {
+        return GNOME_Evolution_Addressbook_AuthenticationRequired;
+    } else
+    if (http_error == 403) {
+        return GNOME_Evolution_Addressbook_AuthenticationFailed;
+    } else {
+        return GNOME_Evolution_Addressbook_OtherError;
+    }
+}
+

Added: trunk/addressbook/backends/google/e-book-backend-google.h
==============================================================================
--- (empty file)
+++ trunk/addressbook/backends/google/e-book-backend-google.h	Mon Jul 21 03:57:49 2008
@@ -0,0 +1,52 @@
+/* e-book-backend-google.h - Google contact backendy.
+ *
+ * Copyright (C) 2008 Joergen Scheibengruber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Joergen Scheibengruber <joergen.scheibengruber AT googlemail.com>
+ */
+
+#ifndef __E_BOOK_BACKEND_GOOGLE_H__
+#define __E_BOOK_BACKEND_GOOGLE_H__
+
+#include <libedata-book/e-book-backend-sync.h>
+
+#define E_TYPE_BOOK_BACKEND_GOOGLE         (e_book_backend_google_get_type ())
+#define E_BOOK_BACKEND_GOOGLE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_BOOK_BACKEND_GOOGLE, EBookBackendGoogle))
+#define E_BOOK_BACKEND_GOOGLE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), E_TYPE_BOOK_BACKEND_GOOGLE, EBookBackendGoogleClass))
+#define E_IS_BOOK_BACKEND_GOOGLE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BOOK_BACKEND_GOOGLE))
+#define E_IS_BOOK_BACKEND_GOOGLE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BOOK_BACKEND_GOOGLE))
+#define E_BOOK_BACKEND_GOOGLE_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_BOOK_BACKEND_GOOGLE, EBookBackendGoogleClass))
+
+typedef struct _EBookBackendGoogle        EBookBackendGoogle;
+typedef struct _EBookBackendGoogleClass   EBookBackendGoogleClass;
+typedef struct _EBookBackendGooglePrivate EBookBackendGooglePrivate;
+
+struct _EBookBackendGoogle
+{
+    EBookBackendSync                   parent_object;
+    EBookBackendGooglePrivate *priv;
+};
+
+struct _EBookBackendGoogleClass
+{
+    EBookBackendSyncClass              parent_class;
+};
+
+EBookBackend *e_book_backend_google_new      (void);
+GType         e_book_backend_google_get_type (void);
+
+#endif /* __E_BOOK_BACKEND_GOOGLE_H__ */

Added: trunk/addressbook/backends/google/util.c
==============================================================================
--- (empty file)
+++ trunk/addressbook/backends/google/util.c	Mon Jul 21 03:57:49 2008
@@ -0,0 +1,819 @@
+/* util.c - Google contact backend utility functions.
+ *
+ * Copyright (C) 2008 Joergen Scheibengruber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Joergen Scheibengruber <joergen.scheibengruber AT googlemail.com>
+ */
+
+#include <string.h>
+#include <libsoup/soup.h>
+#include "util.h"
+
+static EVCardAttribute*
+attribute_from_gdata_entry_email_address  (GDataEntryEmailAddress  *email);
+
+static EVCardAttribute*
+attribute_from_gdata_entry_im_address     (GDataEntryIMAddress     *im);
+
+static EVCardAttribute*
+attribute_from_gdata_entry_phone_number   (GDataEntryPhoneNumber   *number);
+
+static EVCardAttribute*
+attribute_from_gdata_entry_postal_address (GDataEntryPostalAddress *address);
+
+static GDataEntryEmailAddress*
+gdata_entry_email_address_from_attribute  (EVCardAttribute         *attr,
+                                           gboolean                 primary);
+
+static GDataEntryIMAddress*
+gdata_entry_im_address_from_attribute     (EVCardAttribute         *attr,
+                                           gboolean                 primary);
+
+static GDataEntryPhoneNumber*
+gdata_entry_phone_number_from_attribute   (EVCardAttribute         *attr,
+                                           gboolean                 primary);
+
+static GDataEntryPostalAddress*
+gdata_entry_postal_address_from_attribute (EVCardAttribute         *attr,
+                                           gboolean                 primary);
+
+static GList*
+name_values_from_fullname   (const char *fullname);
+
+static char*
+fullname_from_name_values   (GList      *values);
+
+static gboolean
+is_known_google_im_protocol (const char *protocol);
+
+GDataEntry* gdata_entry_create_from_vcard (const char *vcard_str)
+{
+    GDataEntry *entry;
+    EVCard *vcard;
+
+    vcard = e_vcard_new_from_string (vcard_str);
+    if (NULL == vcard)
+        return NULL;
+    entry = gdata_entry_create_from_e_vcard (vcard);
+
+    g_object_unref (vcard);
+
+    return entry;
+}
+
+GDataEntry* gdata_entry_create_from_e_vcard (EVCard *vcard)
+{
+    GDataEntry *entry;
+    GDataEntryCategory *category;
+
+    entry = gdata_entry_new ();
+
+    category = g_new0 (GDataEntryCategory, 1);
+    category->scheme = g_strdup ("http://schemas.google.com/g/2005#kind";);
+    category->term = g_strdup ("http://schemas.google.com/contact/2008#contact";); 
+    gdata_entry_set_categories (entry, g_slist_append (NULL, category));
+
+    if (gdata_entry_update_from_e_vcard (entry, vcard))
+        return entry;
+
+    g_object_unref (entry);
+
+    return NULL;
+}
+
+gboolean gdata_entry_update_from_vcard (GDataEntry *entry,
+                                        const char *vcard_str)
+{
+    EVCard *vcard;
+
+    vcard = e_vcard_new_from_string (vcard_str);
+    if (NULL == vcard)
+        return FALSE;
+    gdata_entry_update_from_e_vcard (entry, vcard);
+
+    g_object_unref (vcard);
+
+    return TRUE;
+}
+
+gboolean gdata_entry_update_from_e_vcard (GDataEntry *entry,
+                                          EVCard     *vcard)
+{
+    GList *attributes, *iter;
+    char *fullname = NULL;
+    GSList *email_addresses = NULL;
+    GSList *im_addresses = NULL;
+    GSList *phone_numbers = NULL;
+    GSList *postal_addresses = NULL;
+    
+    attributes = e_vcard_get_attributes (vcard);
+
+    for (iter = attributes; iter; iter = iter->next) {
+        EVCardAttribute *attr;
+        const char *name;
+
+        attr = iter->data;
+        name = e_vcard_attribute_get_name (attr);
+
+        /* N */
+        if (0 == strcmp (name, EVC_N)) {
+            GList *values;
+
+            if (fullname) {
+                continue;
+            }
+
+            values = e_vcard_attribute_get_values (attr);
+            fullname = fullname_from_name_values (values);
+        } else
+
+        /* FN */
+        if (0 == strcmp (name, EVC_FN)) {
+            GList *values;
+
+            values = e_vcard_attribute_get_values (attr);
+            if (values) {
+                g_free (fullname);
+                fullname = g_strdup (values->data);
+            }
+        } else
+
+        /* EMAIL */
+        if (0 == strcmp (name, EVC_EMAIL)) {
+            GDataEntryEmailAddress *email;
+
+            email = gdata_entry_email_address_from_attribute
+                        (attr, email_addresses == NULL);
+            if (email) {
+                email_addresses = g_slist_append (email_addresses,
+                                                  email);
+            }
+        } else
+
+        /* TEL */
+        if (0 == strcmp (name, EVC_TEL)) {
+            GDataEntryPhoneNumber *number;
+
+            number = gdata_entry_phone_number_from_attribute
+                        (attr, phone_numbers == NULL);
+            if (number) {
+                phone_numbers = g_slist_append (phone_numbers,
+                                                number);
+            }
+        } else
+        
+        /* LABEL */
+        if (0 == strcmp (name, EVC_LABEL)) {
+            GDataEntryPostalAddress *address;
+
+            address = gdata_entry_postal_address_from_attribute
+                        (attr, postal_addresses == NULL);
+            if (address) {
+                postal_addresses = g_slist_append (postal_addresses,
+                                                   address);
+            }
+        } else
+    
+        /* X-IM */
+        if (strncmp (name, "X-", MAX (2, strlen (name))) &&
+            is_known_google_im_protocol (name + 2)) {
+            GDataEntryIMAddress *im;
+
+            im = gdata_entry_im_address_from_attribute
+                        (attr, im_addresses == NULL);
+            if (im) {
+                im_addresses = g_slist_append (im_addresses,
+                                               im);
+            }
+        } else {
+            GList *values;
+
+            values = e_vcard_attribute_get_values (attr);
+            if (values && values->data && ((char*)values->data)[0]) {
+                g_warning ("unsupported vcard field: %s: %s", name, (char*)values->data);
+            }
+        }
+    }
+    gdata_entry_set_title (entry, fullname);
+    gdata_entry_set_email_addresses (entry, email_addresses);
+    gdata_entry_set_im_addresses (entry, im_addresses);
+    gdata_entry_set_phone_numbers (entry, phone_numbers);
+    gdata_entry_set_postal_addresses (entry, postal_addresses);
+
+    return TRUE;
+}
+
+EVCard*
+e_vcard_from_gdata_entry (GDataEntry *entry)
+{
+    EVCard *vcard;
+    EVCardAttribute *attr;
+    GSList *email_addresses, *im_addresses, *phone_numbers, *postal_addresses;
+    const char *name;
+    const char *uid;
+    GSList *itr;
+    GDataEntryEmailAddress *email;
+    GDataEntryIMAddress *im;
+    GDataEntryPhoneNumber *phone_number;
+    GDataEntryPostalAddress *postal_address;
+
+    uid = gdata_entry_get_id (entry);
+    if (NULL == uid) {
+        return NULL;
+    }
+
+    vcard = e_vcard_new ();
+
+    /* UID */
+    attr = e_vcard_attribute_new (NULL, EVC_UID);
+    e_vcard_add_attribute_with_value (vcard, attr, uid);
+    
+    /* FN - TODO: get title */
+    name = gdata_entry_get_title (entry);
+    if (name) {
+        GList *name_values;
+
+        attr = e_vcard_attribute_new (NULL, EVC_FN);
+        e_vcard_add_attribute_with_value (vcard, attr, name);
+
+    /* N */
+        attr = e_vcard_attribute_new (NULL, EVC_N);
+        name_values = name_values_from_fullname (name);
+        while (name_values) {
+            e_vcard_attribute_add_value (attr, name_values->data);
+            g_free (name_values->data);
+            name_values = g_list_delete_link (name_values, name_values);
+        }
+        e_vcard_add_attribute (vcard, attr);
+    }
+
+    /* EMAIL - primary first */
+    email = gdata_entry_get_primary_email_address (entry);
+    attr = attribute_from_gdata_entry_email_address (email);
+    if (attr) {
+        e_vcard_add_attribute (vcard, attr);
+    }
+
+    email_addresses = gdata_entry_get_email_addresses (entry);
+    for (itr = email_addresses; itr; itr = itr->next) {
+        email = itr->data;
+        if (TRUE == email->primary)
+            continue;
+        attr = attribute_from_gdata_entry_email_address (email);
+        if (attr) {
+            e_vcard_add_attribute (vcard, attr);
+        }
+    }
+
+    /* X-IM - primary first */
+    im = gdata_entry_get_primary_im_address (entry);
+    attr = attribute_from_gdata_entry_im_address (im);
+    if (attr) {
+        e_vcard_add_attribute (vcard, attr);
+    }
+    im_addresses = gdata_entry_get_im_addresses (entry);
+    for (itr = im_addresses; itr; itr = itr->next) {
+        im = itr->data;
+        if (TRUE == im->primary)
+            continue;
+        attr = attribute_from_gdata_entry_im_address (im);
+        if (attr) {
+            e_vcard_add_attribute (vcard, attr);
+        }
+    }
+
+    /* TEL - primary first */
+    phone_number = gdata_entry_get_primary_phone_number (entry);
+    attr = attribute_from_gdata_entry_phone_number (phone_number);
+    if (attr) {
+        e_vcard_add_attribute (vcard, attr);
+    }
+    phone_numbers = gdata_entry_get_phone_numbers (entry);
+    for (itr = phone_numbers; itr; itr = itr->next) {
+        phone_number = itr->data;
+        if (TRUE == phone_number->primary)
+            continue;
+        attr = attribute_from_gdata_entry_phone_number (phone_number);
+        if (attr) {
+            e_vcard_add_attribute (vcard, attr);
+        }
+    }
+
+    /* LABEL - primary first TODO: ADR */
+    postal_address = gdata_entry_get_primary_postal_address (entry);
+    attr = attribute_from_gdata_entry_postal_address (postal_address);
+    if (attr) {
+        e_vcard_add_attribute (vcard, attr);
+    }
+    postal_addresses = gdata_entry_get_postal_addresses (entry);
+    for (itr = postal_addresses; itr; itr = itr->next) {
+        postal_address = itr->data;
+        if (TRUE == postal_address->primary)
+            continue;
+        attr = attribute_from_gdata_entry_postal_address (postal_address);
+        if (attr) {
+            e_vcard_add_attribute (vcard, attr);
+        }
+    }
+
+    return vcard;
+}
+
+EContact*
+e_contact_from_gdata_entry (GDataEntry *entry)
+{
+    EContact *contact;
+    char *vcard_str;
+
+    vcard_str = vcard_from_gdata_entry (entry);
+    contact = e_contact_new_from_vcard (vcard_str);
+    g_free (vcard_str);
+
+    return contact;    
+}
+
+char*
+vcard_from_gdata_entry (GDataEntry *entry)
+{
+    EVCard *vcard;
+    char *vcard_str;
+
+    vcard =  e_vcard_from_gdata_entry (entry);
+    vcard_str = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
+    g_object_unref (vcard);
+
+    return vcard_str;
+}
+
+static GList*
+name_values_from_fullname (const char *fullname)
+{
+    const char *comma = NULL;
+    char **names;
+    GList *name_values = NULL;
+
+    if (NULL == fullname)
+        return NULL;
+
+    comma = strstr (fullname, ",");
+    names = g_strsplit_set (fullname, ", ", 3);
+    if (NULL == names[0]) {
+        goto out;
+    }
+    /* Homer */
+    if (NULL == names[1]) {
+        name_values = g_list_append (NULL, names[0]);
+        goto out;
+    }
+    if (comma) {
+    /* Simpson, Homer */
+        name_values = g_list_append (NULL, names[0]);
+        name_values = g_list_append (name_values, names[1]);
+    /* Simpson, Homer J.*/
+        if (names[2]) {
+            name_values = g_list_append (name_values, names[2]);
+        }
+    } else {
+    /* Homer J. Simpson */    
+        if (names[2]) {
+            name_values = g_list_append (NULL, names[2]);
+            name_values = g_list_append (name_values, names[0]);
+            name_values = g_list_append (name_values, names[1]);
+        }
+    /* Homer Simpson */    
+        else {
+            name_values = g_list_append (NULL, names[1]);
+            name_values = g_list_append (name_values, names[0]);
+        }
+    }
+
+out:
+    g_free (names);
+
+    return name_values;
+}
+
+static GString*
+string_prepend_with_space (GString *string, const char *val)
+{
+    if (NULL == val ||
+        0 == val[0])
+        return string;
+    g_string_prepend (string, " ");
+    g_string_prepend (string, val);
+
+    return string;
+}
+
+#if 0
+static GString*
+string_append_with_space (GString *string, const char *val)
+{
+    if (NULL == val ||
+        0 == val[0])
+        return string;
+    string = g_string_append (string, " ");
+    string = g_string_append (string, val);
+
+    return string;
+}
+#endif
+
+static char*
+fullname_from_name_values (GList *values)
+{
+    GString *name;
+    const char *givenname;
+
+    if (NULL == values ||
+        NULL == values->data)
+        return NULL;
+
+    /* Family Name */
+    name = g_string_new (values->data);
+
+    values = values->next;
+    if (NULL == values)
+        goto out;
+
+    /* Given Name */
+    givenname = values->data;
+
+    values = values->next;
+    if (NULL == values) {
+        string_prepend_with_space (name, givenname);
+        goto out;
+    }
+
+    /* Additional Names */
+    string_prepend_with_space (name, values->data);
+    string_prepend_with_space (name, givenname);
+
+    values = values->next;
+    if (NULL == values)
+        goto out;
+
+    /* TODO: Honoric stuff should go into ORG? */
+#if 0
+    /* Honorific Prefixes */
+    string_prepend_with_space (name, values->data);
+
+    values = values->next;
+    if (NULL == values)
+        goto out;
+
+    /* Honorific Suffixes */
+    string_append_with_space (name, values->data);
+#endif
+
+out:
+    return g_string_free (name, FALSE);
+}
+
+char* build_uri (const char *base_uri, ...)
+{
+    va_list params;
+    const char *param;
+    GString *query;
+    const char *separator = "?";
+
+    query = g_string_new (base_uri);
+    va_start (params, base_uri);
+    while (TRUE) {
+        param = va_arg (params, const char*);
+        if (NULL == param) {
+            break;
+        }
+        g_string_append (query, separator);
+        g_string_append (query, param);
+        separator = "&";
+    }
+    va_end (params);
+
+    return g_string_free (query, FALSE);
+
+}
+
+char* build_base_uri (const char* username)
+{
+    const char *format = "http://www.google.com/m8/feeds/contacts/%s/base";;
+    char *esc_username;
+    char *uri;
+
+    esc_username = g_uri_escape_string (username, NULL, FALSE);
+    uri = g_strdup_printf (format, esc_username);
+    g_free (esc_username);
+
+    return uri;
+}
+
+gboolean test_repository_availability (void)
+{
+    SoupSession *session;
+    SoupMessage *message;
+    int http_status;
+    const char *uri = "http://www.google.com/m8/feeds/contacts";;
+
+    session = soup_session_sync_new ();
+    message = soup_message_new (SOUP_METHOD_GET, uri);
+
+    http_status = soup_session_send_message (session, message);
+    __debug__ ("%s: HTTP %d (%s)",
+               G_STRFUNC,
+               http_status,
+               soup_status_get_phrase (http_status));
+    g_object_unref (message);
+    g_object_unref (session);
+
+    /* Everything below 100 means we can't reach www.google.com */
+    return (http_status >= 100);
+}
+
+static char*
+type_from_google_rel_label (const char* rel, const char *label)
+{
+    if (rel) {
+        char *type;
+        type = g_strrstr (rel, "#");
+        if (NULL == type)
+            return NULL;
+        return g_ascii_strup (type + 1, -1);
+    }
+    if (label) {
+        return g_strdup_printf ("X-%s", label);
+    }
+    return NULL;
+}
+
+static void
+google_rel_label_from_type (const char* type, char **rel, char **label)
+{
+    const char *format = "http://schemas.google.com/g/2005#%s";;
+
+    *label = NULL;
+    *rel = NULL;
+    if (NULL == type) {
+        *rel = g_strdup_printf (format, "other");
+        return;
+    }
+
+    if (0 == strcmp (type, "WORK")) {
+        *rel = g_strdup_printf (format, "work");
+        return;
+    }
+    if (0 == strcmp (type, "HOME")) {
+        *rel = g_strdup_printf (format, "home");
+        return;
+    }
+
+    if (0 == strncmp (type, "X-", MIN (2, strlen (type)))) {
+        *label = g_strdup (type + 2);
+        return;
+    }
+
+    *rel = g_strdup_printf (format, "other");
+}
+
+static gboolean
+is_known_google_im_protocol (const char *protocol)
+{
+    const char *known_protocols[] =
+    {
+        "AIM", "MSN", "YAHOO",     "SKYPE", "QQ",
+        "GOOGLE_TALK", "ICQ", "JABBER"
+    };
+    int i;
+
+    if (NULL == protocol)
+        return FALSE;
+    
+    for (i = 0; i < G_N_ELEMENTS (known_protocols); i++) {
+        if (0 == strcmp (known_protocols[i], protocol))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+static char*
+field_name_from_google_im_protocol (const char* google_protocol)
+{
+    char *protocol;
+    if (NULL == google_protocol)
+        return NULL;
+
+    protocol = g_strrstr (google_protocol, "#");
+    if (NULL == protocol)
+        return NULL;
+    return g_strdup_printf ("X-%s", protocol + 1);
+}
+
+static char*
+google_im_protocol_from_field_name (const char* field_name)
+{
+    const char *format = "http://schemas.google.com/g/2005#%s";;
+
+    if (NULL == field_name ||
+        strlen (field_name) < 3) {
+        return NULL;
+    }
+
+    return g_strdup_printf (format, field_name + 2);
+}
+
+static EVCardAttribute*
+attribute_from_gdata_entry_email_address (GDataEntryEmailAddress *email)
+{
+    EVCardAttribute *attr;
+    EVCardAttributeParam *param;
+    char *type;
+
+    if (NULL == email || NULL == email->address)
+        return NULL;;
+
+    attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
+    type = type_from_google_rel_label (email->rel, email->label);
+    if (type) {
+        param = e_vcard_attribute_param_new ("TYPE");
+        e_vcard_attribute_add_param_with_value (attr, param, type);
+        g_free (type);
+    }
+    e_vcard_attribute_add_value (attr, email->address);
+    return attr;
+}
+
+static EVCardAttribute*
+attribute_from_gdata_entry_im_address (GDataEntryIMAddress *im)
+{
+    EVCardAttribute *attr;
+    EVCardAttributeParam *param;
+    char *type;
+    char *field_name;
+
+    if (NULL == im || NULL == im->address)
+        return NULL;;
+
+    field_name = field_name_from_google_im_protocol (im->protocol);
+    if (NULL == field_name)
+        return NULL;
+
+    attr = e_vcard_attribute_new (NULL, field_name);
+    type = type_from_google_rel_label (im->rel, im->label);
+    if (type) {
+        param = e_vcard_attribute_param_new ("TYPE");
+        e_vcard_attribute_add_param_with_value (attr, param, type);
+        g_free (type);
+    }
+    e_vcard_attribute_add_value (attr, im->address);
+    return attr;
+}
+
+static EVCardAttribute*
+attribute_from_gdata_entry_phone_number (GDataEntryPhoneNumber *number)
+{
+    EVCardAttribute *attr;
+    EVCardAttributeParam *param;
+    char *type;
+
+    if (NULL == number || NULL == number->number)
+        return NULL;;
+
+    attr = e_vcard_attribute_new (NULL, EVC_TEL);
+    /* TODO: This needs more work */
+    type = type_from_google_rel_label (number->rel, number->label);
+    if (type) {
+        param = e_vcard_attribute_param_new ("TYPE");
+        e_vcard_attribute_add_param_with_value (attr, param, type);
+        g_free (type);
+    }
+    e_vcard_attribute_add_value (attr, number->number);
+    return attr;
+}
+
+static EVCardAttribute*
+attribute_from_gdata_entry_postal_address (GDataEntryPostalAddress *address)
+{
+    EVCardAttribute *attr;
+    EVCardAttributeParam *param;
+    char *type;
+
+    if (NULL == address || NULL == address->address)
+        return NULL;;
+
+    attr = e_vcard_attribute_new (NULL, EVC_LABEL);
+    /* TODO: This needs more work */
+    type = type_from_google_rel_label (address->rel, address->label);
+    if (type) {
+        param = e_vcard_attribute_param_new ("TYPE");
+        e_vcard_attribute_add_param_with_value (attr, param, type);
+        g_free (type);
+    }
+    e_vcard_attribute_add_value (attr, address->address);
+    return attr;
+}
+
+static GDataEntryEmailAddress*
+gdata_entry_email_address_from_attribute (EVCardAttribute *attr, gboolean primary)
+{
+    GDataEntryEmailAddress *email = NULL;
+    GList *values;
+
+    values = e_vcard_attribute_get_values (attr);
+    if (values) {
+        GList *param_values;
+        const char *type;
+
+        param_values = e_vcard_attribute_get_param (attr, "TYPE");
+        type = param_values ? param_values->data : NULL;
+        email = g_new0 (GDataEntryEmailAddress, 1);
+        email->address = g_strdup (values->data);
+        google_rel_label_from_type (type, &email->rel, &email->label);
+        email->primary = primary;
+    }
+
+    return email;
+}
+
+static GDataEntryIMAddress*
+gdata_entry_im_address_from_attribute (EVCardAttribute *attr, gboolean primary)
+{
+    GDataEntryIMAddress *im = NULL;
+    GList *values;
+    const char *name;
+
+    name = e_vcard_attribute_get_name (attr);
+
+    values = e_vcard_attribute_get_values (attr);
+    if (values) {
+        GList *param_values;
+        const char *type;
+
+        param_values = e_vcard_attribute_get_param (attr, "TYPE");
+        type = param_values ? param_values->data : NULL;
+        im = g_new0 (GDataEntryIMAddress, 1);
+        im->address = g_strdup (values->data);
+        google_rel_label_from_type (type, &im->rel, &im->label);
+        im->primary = primary;
+        im->protocol = google_im_protocol_from_field_name (name);
+    }
+
+    return im;
+}
+
+static GDataEntryPhoneNumber*
+gdata_entry_phone_number_from_attribute (EVCardAttribute *attr, gboolean primary)
+{
+    GDataEntryPhoneNumber *number = NULL;
+    GList *values;
+
+    values = e_vcard_attribute_get_values (attr);
+    if (values) {
+        GList *param_values;
+        const char *type;
+
+        param_values = e_vcard_attribute_get_param (attr, "TYPE");
+        type = param_values ? param_values->data : NULL;
+        number = g_new0 (GDataEntryPhoneNumber, 1);
+        number->number = g_strdup (values->data);
+        /* TODO: this needs more work */
+        google_rel_label_from_type (type, &number->rel, &number->label);
+        number->primary = primary;
+    }
+
+    return number;
+}
+
+static GDataEntryPostalAddress*
+gdata_entry_postal_address_from_attribute (EVCardAttribute *attr, gboolean primary)
+{
+    GDataEntryPostalAddress *address = NULL;
+    GList *values;
+
+    values = e_vcard_attribute_get_values (attr);
+    if (values) {
+        GList *param_values;
+        const char *type;
+
+        param_values = e_vcard_attribute_get_param (attr, "TYPE");
+        type = param_values ? param_values->data : NULL;
+        address = g_new0 (GDataEntryPostalAddress, 1);
+        address->address = g_strdup (values->data);
+        google_rel_label_from_type (type, &address->rel, &address->label);
+        address->primary = primary;
+    }
+
+    return address;
+}

Added: trunk/addressbook/backends/google/util.h
==============================================================================
--- (empty file)
+++ trunk/addressbook/backends/google/util.h	Mon Jul 21 03:57:49 2008
@@ -0,0 +1,52 @@
+/* util.h - Google contact backend utility functions.
+ *
+ * Copyright (C) 2008 Joergen Scheibengruber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Joergen Scheibengruber <joergen.scheibengruber AT googlemail.com>
+ */
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#include <libebook/e-vcard.h>
+#include <libebook/e-contact.h>
+#include <servers/google/libgdata/gdata-entry.h>
+
+char*       vcard_from_gdata_entry          (GDataEntry *entry);
+EVCard*     e_vcard_from_gdata_entry        (GDataEntry *entry);
+EContact*   e_contact_from_gdata_entry      (GDataEntry *entry);
+
+GDataEntry* gdata_entry_create_from_vcard   (const char *vcard_str);
+GDataEntry* gdata_entry_create_from_e_vcard (EVCard *vcard);
+gboolean    gdata_entry_update_from_vcard   (GDataEntry *entry,
+                                             const char *vcard_str);
+gboolean    gdata_entry_update_from_e_vcard (GDataEntry *entry,
+                                             EVCard     *vcard);
+
+
+char*       build_uri                       (const char *base_uri, ...);
+char*       build_base_uri                  (const char *username);
+
+gboolean    test_repository_availability    (void);
+
+extern gboolean __e_book_backend_google_debug__;
+
+#define __debug__(...) (__e_book_backend_google_debug__ ? \
+                       g_log (G_LOG_DOMAIN,         \
+                              G_LOG_LEVEL_DEBUG,    \
+                              __VA_ARGS__) : 0 )
+
+#endif

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Mon Jul 21 03:57:49 2008
@@ -1667,6 +1667,7 @@
 addressbook/backends/file/Makefile
 addressbook/backends/vcf/Makefile
 addressbook/backends/ldap/Makefile
+addressbook/backends/google/Makefile
 addressbook/backends/groupwise/Makefile
 addressbook/tests/Makefile
 addressbook/tests/ebook/Makefile



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