summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiegfried-Angel Gevatter Pujals <rainct@ubuntu.com>2012-06-30 14:54:48 +0200
committerSiegfried-Angel Gevatter Pujals <rainct@ubuntu.com>2012-06-30 14:54:48 +0200
commit61208d0a4d41406aa70f44f963cd80e6b7127a0b (patch)
treea94108a3abca25814aa41b1c26b020f47dd5673f
parent5f39c94c64fa5cc051765df1f544f63caca49c07 (diff)
Start writing Google Docs data-source
-rw-r--r--src/Makefile.am7
-rw-r--r--src/gd-gdata-goa-authorizer.c539
-rw-r--r--src/gd-gdata-goa-authorizer.h66
-rw-r--r--src/google-authorizer.vapi5
-rw-r--r--src/google-provider.vala68
-rw-r--r--src/zeitgeist-datahub.vala1
6 files changed, 685 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index de5d208..7276047 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,7 +10,11 @@ VALAFLAGS = \
--pkg json-glib-1.0 \
--pkg telepathy-glib \
--pkg zeitgeist-1.0 \
+ --pkg goa-1.0 \
+ --vapidir $(top_srcdir)/src \
+ -X "-DGOA_API_IS_SUBJJECT_TO_CHANGE" \
glib-extra.vapi \
+ google-authorizer.vapi \
$(top_srcdir)/config.vapi \
$(NULL)
@@ -27,9 +31,9 @@ zeitgeist_datahub_SOURCES = \
kde-recent-document-provider.vala \
recent-manager-provider.vala \
telepathy-observer.vala \
+ google-provider.vala \
utils.vala \
zeitgeist-datahub.vala \
- $(optional_zeitgeist_datahub_SOURCES) \
$(NULL)
xdgautostart_in_files = \
@@ -44,4 +48,5 @@ CLEANFILES = zeitgeist-datahub.desktop
EXTRA_DIST = \
glib-extra.vapi \
+ google-authorizer.vapi \
$(xdgautostart_in_files)
diff --git a/src/gd-gdata-goa-authorizer.c b/src/gd-gdata-goa-authorizer.c
new file mode 100644
index 0000000..b4ede0e
--- /dev/null
+++ b/src/gd-gdata-goa-authorizer.c
@@ -0,0 +1,539 @@
+/*
+ * e-gdata-goa-authorizer.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gd-gdata-goa-authorizer.h"
+
+#include <string.h>
+#include <oauth.h>
+
+#define GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), GD_TYPE_GDATA_GOA_AUTHORIZER, GdGDataGoaAuthorizerPrivate))
+
+struct _GdGDataGoaAuthorizerPrivate {
+
+ /* GDataAuthorizer methods must be thread-safe. */
+ GMutex mutex;
+
+ /* GoaObject is already thread-safe. */
+ GoaObject *goa_object;
+
+ /* These members are protected by the GMutex. */
+ gchar *access_token;
+ gchar *access_token_secret;
+ GHashTable *authorization_domains;
+};
+
+enum {
+ PROP_0,
+ PROP_GOA_OBJECT
+};
+
+/* Forward Declarations */
+static void gd_gdata_goa_authorizer_interface_init
+ (GDataAuthorizerInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ GdGDataGoaAuthorizer,
+ gd_gdata_goa_authorizer,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (
+ GDATA_TYPE_AUTHORIZER,
+ gd_gdata_goa_authorizer_interface_init))
+
+static GHashTable *
+gdata_goa_authorizer_get_parameters (SoupMessage *message,
+ const gchar *consumer_key,
+ const gchar *consumer_secret,
+ const gchar *access_token,
+ const gchar *access_token_secret)
+{
+ GString *query;
+ GString *base_string;
+ GString *signing_key;
+ GHashTable *parameters;
+ GHashTable *hash_table;
+ GHashTableIter iter;
+ SoupURI *soup_uri;
+ GList *keys, *link;
+ gchar *string;
+ gchar *request_uri;
+ gpointer key, value;
+
+ parameters = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) g_free);
+
+ /* soup_form_decode() uses an awkward allocation style for
+ * its hash table entries, so it's easier to copy its content
+ * into our own hash table than try to use the returned hash
+ * table directly. */
+
+ soup_uri = soup_message_get_uri (message);
+
+ if (soup_uri->query != NULL) {
+ hash_table = soup_form_decode (soup_uri->query);
+ g_hash_table_iter_init (&iter, hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ key = (gpointer) g_intern_string (key);
+ g_hash_table_insert (parameters, key, g_strdup (value));
+ }
+ g_hash_table_destroy (hash_table);
+ }
+
+ /* Add OAuth parameters. */
+
+ key = (gpointer) "oauth_version";
+ g_hash_table_insert (parameters, key, g_strdup ("1.0"));
+
+ string = oauth_gen_nonce ();
+ key = (gpointer) "oauth_nonce";
+ g_hash_table_insert (parameters, key, g_strdup (string));
+ free (string); /* do not use g_free () */
+
+ key = (gpointer) "oauth_timestamp";
+ string = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) time (NULL));
+ g_hash_table_insert (parameters, key, string); /* takes ownership */
+
+ key = (gpointer) "oauth_consumer_key";
+ g_hash_table_insert (parameters, key, g_strdup (consumer_key));
+
+ key = (gpointer) "oauth_token";
+ g_hash_table_insert (parameters, key, g_strdup (access_token));
+
+ key = (gpointer) "oauth_signature_method";
+ g_hash_table_insert (parameters, key, g_strdup ("HMAC-SHA1"));
+
+ /* Build the query part of the signature base string. */
+
+ query = g_string_sized_new (512);
+ keys = g_hash_table_get_keys (parameters);
+ keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
+ for (link = keys; link != NULL; link = g_list_next (link)) {
+ const gchar *key = link->data;
+ const gchar *val;
+
+ val = g_hash_table_lookup (parameters, key);
+
+ if (link != keys)
+ g_string_append_c (query, '&');
+
+ g_string_append_uri_escaped (query, key, NULL, FALSE);
+ g_string_append_c (query, '=');
+ g_string_append_uri_escaped (query, val, NULL, FALSE);
+ }
+ g_list_free (keys);
+
+ /* Build the signature base string. */
+
+ soup_uri = soup_uri_copy (soup_uri);
+ soup_uri_set_query (soup_uri, NULL);
+ soup_uri_set_fragment (soup_uri, NULL);
+ request_uri = soup_uri_to_string (soup_uri, FALSE);
+ soup_uri_free (soup_uri);
+
+ base_string = g_string_sized_new (512);
+ g_string_append_uri_escaped (base_string, message->method, NULL, FALSE);
+ g_string_append_c (base_string, '&');
+ g_string_append_uri_escaped (base_string, request_uri, NULL, FALSE);
+ g_string_append_c (base_string, '&');
+ g_string_append_uri_escaped (base_string, query->str, NULL, FALSE);
+
+ /* Build the HMAC-SHA1 signing key. */
+
+ signing_key = g_string_sized_new (512);
+ g_string_append_uri_escaped (
+ signing_key, consumer_secret, NULL, FALSE);
+ g_string_append_c (signing_key, '&');
+ g_string_append_uri_escaped (
+ signing_key, access_token_secret, NULL, FALSE);
+
+ /* Sign the request. */
+
+ key = (gpointer) "oauth_signature";
+ string = oauth_sign_hmac_sha1 (base_string->str, signing_key->str);
+ g_hash_table_insert (parameters, key, g_strdup (string));
+ free (string); /* do not use g_free () */
+
+ g_free (request_uri);
+
+ g_string_free (query, TRUE);
+ g_string_free (base_string, TRUE);
+ g_string_free (signing_key, TRUE);
+
+ return parameters;
+}
+
+static void
+gdata_goa_authorizer_add_authorization (GDataAuthorizer *authorizer,
+ SoupMessage *message)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+ GoaOAuthBased *goa_oauth_based;
+ GHashTable *parameters;
+ GString *authorization;
+ const gchar *consumer_key;
+ const gchar *consumer_secret;
+ gint ii;
+
+ const gchar *oauth_keys[] = {
+ "oauth_version",
+ "oauth_nonce",
+ "oauth_timestamp",
+ "oauth_consumer_key",
+ "oauth_token",
+ "oauth_signature_method",
+ "oauth_signature"
+ };
+
+ /* This MUST be called with the mutex already locked. */
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ /* We can't add an Authorization header without an access token.
+ * Let the request fail. GData should refresh us if it gets back
+ * a "401 Authorization required" response from Google, and then
+ * automatically retry the request. */
+ if (priv->access_token == NULL)
+ return;
+
+ goa_oauth_based = goa_object_get_oauth_based (priv->goa_object);
+
+ consumer_key = goa_oauth_based_get_consumer_key (goa_oauth_based);
+ consumer_secret = goa_oauth_based_get_consumer_secret (goa_oauth_based);
+
+ parameters = gdata_goa_authorizer_get_parameters (
+ message,
+ consumer_key,
+ consumer_secret,
+ priv->access_token,
+ priv->access_token_secret);
+
+ authorization = g_string_new ("OAuth ");
+
+ for (ii = 0; ii < G_N_ELEMENTS (oauth_keys); ii++) {
+ const gchar *key;
+ const gchar *val;
+
+ key = oauth_keys[ii];
+ val = g_hash_table_lookup (parameters, key);
+
+ if (ii > 0)
+ g_string_append (authorization, ", ");
+
+ g_string_append (authorization, key);
+ g_string_append_c (authorization, '=');
+ g_string_append_c (authorization, '"');
+ g_string_append_uri_escaped (authorization, val, NULL, FALSE);
+ g_string_append_c (authorization, '"');
+ }
+
+ /* Use replace here, not append, to make sure
+ * there's only one "Authorization" header. */
+ soup_message_headers_replace (
+ message->request_headers,
+ "Authorization", authorization->str);
+
+ g_string_free (authorization, TRUE);
+ g_hash_table_destroy (parameters);
+
+ g_object_unref (goa_oauth_based);
+}
+
+static gboolean
+gdata_goa_authorizer_is_authorized (GDataAuthorizer *authorizer,
+ GDataAuthorizationDomain *domain)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+
+ /* This MUST be called with the mutex already locked. */
+
+ if (domain == NULL)
+ return TRUE;
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+ domain = g_hash_table_lookup (priv->authorization_domains, domain);
+
+ return (domain != NULL);
+}
+
+static void
+gdata_goa_authorizer_set_goa_object (GdGDataGoaAuthorizer *authorizer,
+ GoaObject *goa_object)
+{
+ g_return_if_fail (GOA_IS_OBJECT (goa_object));
+ g_return_if_fail (authorizer->priv->goa_object == NULL);
+
+ authorizer->priv->goa_object = g_object_ref (goa_object);
+}
+
+static void
+gdata_goa_authorizer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_GOA_OBJECT:
+ gdata_goa_authorizer_set_goa_object (
+ GD_GDATA_GOA_AUTHORIZER (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+gdata_goa_authorizer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_GOA_OBJECT:
+ g_value_set_object (
+ value,
+ gd_gdata_goa_authorizer_get_goa_object (
+ GD_GDATA_GOA_AUTHORIZER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+gdata_goa_authorizer_dispose (GObject *object)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (object);
+
+ if (priv->goa_object != NULL) {
+ g_object_unref (priv->goa_object);
+ priv->goa_object = NULL;
+ }
+
+ g_hash_table_remove_all (priv->authorization_domains);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (gd_gdata_goa_authorizer_parent_class)->dispose (object);
+}
+
+static void
+gdata_goa_authorizer_finalize (GObject *object)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (object);
+
+ g_mutex_clear (&priv->mutex);
+ g_free (priv->access_token);
+ g_free (priv->access_token_secret);
+ g_hash_table_destroy (priv->authorization_domains);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (gd_gdata_goa_authorizer_parent_class)->finalize (object);
+}
+
+static void
+gdata_goa_authorizer_constructed (GObject *object)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+ GType service_type;
+ GList *domains;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (gd_gdata_goa_authorizer_parent_class)->
+ constructed (object);
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (object);
+
+ /* XXX We would need to generalize this to make the class
+ * reusable for other service types, probably by adding
+ * a "service-type" constructor property. */
+ service_type = GDATA_TYPE_DOCUMENTS_SERVICE;
+ domains = gdata_service_get_authorization_domains (service_type);
+
+ while (domains != NULL) {
+ g_hash_table_insert (
+ priv->authorization_domains,
+ g_object_ref (domains->data),
+ domains->data);
+ domains = g_list_delete_link (domains, domains);
+ }
+}
+
+static void
+gdata_goa_authorizer_process_request (GDataAuthorizer *authorizer,
+ GDataAuthorizationDomain *domain,
+ SoupMessage *message)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ g_mutex_lock (&priv->mutex);
+
+ if (gdata_goa_authorizer_is_authorized (authorizer, domain))
+ gdata_goa_authorizer_add_authorization (authorizer, message);
+
+ g_mutex_unlock (&priv->mutex);
+}
+
+static gboolean
+gdata_goa_authorizer_is_authorized_for_domain (GDataAuthorizer *authorizer,
+ GDataAuthorizationDomain *domain)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+ gboolean authorized;
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ g_mutex_lock (&priv->mutex);
+
+ authorized = gdata_goa_authorizer_is_authorized (authorizer, domain);
+
+ g_mutex_unlock (&priv->mutex);
+
+ return authorized;
+}
+
+static gboolean
+gdata_goa_authorizer_refresh_authorization (GDataAuthorizer *authorizer,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GdGDataGoaAuthorizerPrivate *priv;
+ GoaOAuthBased *goa_oauth_based;
+ GoaAccount *goa_account;
+ gboolean success = TRUE;
+
+ priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ g_mutex_lock (&priv->mutex);
+
+ g_free (priv->access_token);
+ priv->access_token = NULL;
+
+ g_free (priv->access_token_secret);
+ priv->access_token_secret = NULL;
+
+ goa_account = goa_object_get_account (priv->goa_object);
+ goa_oauth_based = goa_object_get_oauth_based (priv->goa_object);
+
+ success &= goa_account_call_ensure_credentials_sync (
+ goa_account, NULL, cancellable, error);
+
+ success &= goa_oauth_based_call_get_access_token_sync (
+ goa_oauth_based, &priv->access_token,
+ &priv->access_token_secret, NULL, cancellable, error);
+
+ g_object_unref (goa_account);
+ g_object_unref (goa_oauth_based);
+
+ g_mutex_unlock (&priv->mutex);
+
+ return success;
+}
+
+static void
+gd_gdata_goa_authorizer_class_init (GdGDataGoaAuthorizerClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (GdGDataGoaAuthorizerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = gdata_goa_authorizer_set_property;
+ object_class->get_property = gdata_goa_authorizer_get_property;
+ object_class->dispose = gdata_goa_authorizer_dispose;
+ object_class->finalize = gdata_goa_authorizer_finalize;
+ object_class->constructed = gdata_goa_authorizer_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_GOA_OBJECT,
+ g_param_spec_object (
+ "goa-object",
+ "GoaObject",
+ "The GOA account to authenticate",
+ GOA_TYPE_OBJECT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gd_gdata_goa_authorizer_interface_init (GDataAuthorizerInterface *interface)
+{
+ interface->process_request =
+ gdata_goa_authorizer_process_request;
+ interface->is_authorized_for_domain =
+ gdata_goa_authorizer_is_authorized_for_domain;
+ interface->refresh_authorization =
+ gdata_goa_authorizer_refresh_authorization;
+}
+
+static void
+gd_gdata_goa_authorizer_init (GdGDataGoaAuthorizer *authorizer)
+{
+ GHashTable *authorization_domains;
+
+ authorization_domains = g_hash_table_new_full (
+ (GHashFunc) g_direct_hash,
+ (GEqualFunc) g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) NULL);
+
+ authorizer->priv = GD_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+ g_mutex_init (&authorizer->priv->mutex);
+ authorizer->priv->authorization_domains = authorization_domains;
+}
+
+/**
+ * gd_gdata_goa_authorizer_new:
+ * @goa_object:
+ *
+ * Returns: (transfer full):
+ */
+GdGDataGoaAuthorizer *
+gd_gdata_goa_authorizer_new (GoaObject *goa_object)
+{
+ g_return_val_if_fail (GOA_IS_OBJECT (goa_object), NULL);
+
+ return g_object_new (
+ GD_TYPE_GDATA_GOA_AUTHORIZER,
+ "goa-object", goa_object, NULL);
+}
+
+/**
+ * gd_gdata_goa_authorizer_get_goa_object:
+ * @authorizer:
+ *
+ * Returns: (transfer none):
+ */
+GoaObject *
+gd_gdata_goa_authorizer_get_goa_object (GdGDataGoaAuthorizer *authorizer)
+{
+ g_return_val_if_fail (GD_IS_GDATA_GOA_AUTHORIZER (authorizer), NULL);
+
+ return authorizer->priv->goa_object;
+}
diff --git a/src/gd-gdata-goa-authorizer.h b/src/gd-gdata-goa-authorizer.h
new file mode 100644
index 0000000..26c8faa
--- /dev/null
+++ b/src/gd-gdata-goa-authorizer.h
@@ -0,0 +1,66 @@
+/*
+ * e-gdata-goa-authorizer.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GD_GDATA_GOA_AUTHORIZER_H
+#define GD_GDATA_GOA_AUTHORIZER_H
+
+#include <gdata/gdata.h>
+#include <goa/goa.h>
+
+/* Standard GObject macros */
+#define GD_TYPE_GDATA_GOA_AUTHORIZER \
+ (gd_gdata_goa_authorizer_get_type ())
+#define GD_GDATA_GOA_AUTHORIZER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), GD_TYPE_GDATA_GOA_AUTHORIZER, GdGDataGoaAuthorizer))
+#define GD_GDATA_GOA_AUTHORIZER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), GD_TYPE_GDATA_GOA_AUTHORIZER, GdGDataGoaAuthorizerClass))
+#define GD_IS_GDATA_GOA_AUTHORIZER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), GD_TYPE_GDATA_GOA_AUTHORIZER))
+#define GD_IS_GDATA_GOA_AUTHORIZER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), GD_TYPE_GDATA_GOA_AUTHORIZER))
+#define GD_GDATA_GOA_AUTHORIZER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), GD_TYPE_GDATA_GOA_AUTHORIZER, GdGDataGoaAuthorizerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GdGDataGoaAuthorizer GdGDataGoaAuthorizer;
+typedef struct _GdGDataGoaAuthorizerClass GdGDataGoaAuthorizerClass;
+typedef struct _GdGDataGoaAuthorizerPrivate GdGDataGoaAuthorizerPrivate;
+
+struct _GdGDataGoaAuthorizer {
+ GObject parent;
+ GdGDataGoaAuthorizerPrivate *priv;
+};
+
+struct _GdGDataGoaAuthorizerClass {
+ GObjectClass parent_class;
+};
+
+GType gd_gdata_goa_authorizer_get_type (void);
+GdGDataGoaAuthorizer *
+ gd_gdata_goa_authorizer_new
+ (GoaObject *goa_object);
+GoaObject * gd_gdata_goa_authorizer_get_goa_object
+ (GdGDataGoaAuthorizer *authorizer);
+
+#endif /* GD_GDATA_GOA_AUTHORIZER_H */
diff --git a/src/google-authorizer.vapi b/src/google-authorizer.vapi
new file mode 100644
index 0000000..487cd6d
--- /dev/null
+++ b/src/google-authorizer.vapi
@@ -0,0 +1,5 @@
+[CCode (cprefix = "GdGDataGoaAuthorizer", lower_case_cprefix = "gd_gdata_goa_authorizer_", cheader_filename = "gd-gdata-goa-authorizer.h")]
+class GoogleAuthorizer {
+ public GoogleAuthorizer (GoaObject *goa_object);
+ public GoaObject* get_goa_object ();
+}
diff --git a/src/google-provider.vala b/src/google-provider.vala
new file mode 100644
index 0000000..c4f67ac
--- /dev/null
+++ b/src/google-provider.vala
@@ -0,0 +1,68 @@
+/*
+ * Zeitgeist
+ *
+ * Copyright (C) 2011-2012 Collabora Ltd.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by Siegfried-A. Gevatter <siegfried.gevatter@collabora.co.uk>
+ *
+ */
+
+using Zeitgeist;
+
+public class GoogleProvider : DataProvider
+{
+ public GoogleProvider (DataHub datahub) throws GLib.Error
+ {
+ GLib.Object (unique_id: "com.zeitgeist-project,datahub,google",
+ name: "Google Data-Source",
+ description: "Logs events from Google services",
+ datahub: datahub);
+ }
+
+ // if vala didn't have bug in construct-only properties, the properties
+ // would be construct-only
+ public override string unique_id { get; construct set; }
+ public override string name { get; construct set; }
+ public override string description { get; construct set; }
+
+ public override DataHub datahub { get; construct set; }
+ public override bool enabled { get; set; default = true; }
+ public override bool register { get; construct set; default = true; }
+
+ private GoaClient goa_client;
+
+ construct
+ {
+ last_timestamp = 0;
+ }
+
+ public override void start ()
+ {
+ if (goa_client == null)
+ {
+ goa_client = new GoaClient.sync();
+ foreach (GoaObject account in goa_client.get_accounts ())
+ {
+ warning ("HI! FOUND ACCOUNT!");
+ }
+ }
+ }
+
+ public override void stop ()
+ {
+ }
+
+}
diff --git a/src/zeitgeist-datahub.vala b/src/zeitgeist-datahub.vala
index a336cbb..ed28fc9 100644
--- a/src/zeitgeist-datahub.vala
+++ b/src/zeitgeist-datahub.vala
@@ -112,6 +112,7 @@ public class DataHub : Object, DataHubService
providers.prepend (new RecentManagerGtk (this));
providers.prepend (new RecentDocumentsKDE (this));
providers.prepend (new TelepathyObserver (this));
+ providers.prepend (new GoogleProvider (this));
if (Config.DOWNLOADS_MONITOR_ENABLED)
providers.prepend (new DownloadsDirectoryMonitor (this));