summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2011-04-25 17:36:42 -0400
committerDavid Zeuthen <davidz@redhat.com>2011-04-25 17:36:42 -0400
commit0ce85dce3d531db24961467af518de3dc47b9a82 (patch)
treeebecb862395ef1ad7b6d0053921a45ceec2d3112
parenta789d5b5e8338939ffc45ebf80ea257cb00f6caa (diff)
Add org.gnome.OnlineAccounts.AccessTokenBased interface
This means that e.g. gdbus call --session --dest org.gnome.OnlineAccounts --object-path /org/gnome/OnlineAccounts/Accounts/account_1303766726 --method org.gnome.OnlineAccounts.AccessTokenBased.GetAccessToken ('1/nGak8-9mV69ht19pqyP6CGgeckEhQB3x43szhGL0BLk', 3600) works. Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r--data/dbus-interfaces.xml26
-rw-r--r--doc/goa-docs.xml2
-rw-r--r--doc/goa-sections.txt50
-rw-r--r--doc/goa.types1
-rw-r--r--src/daemon/Makefile.am4
-rw-r--r--src/daemon/goadaemon.c127
-rw-r--r--src/goa/goabackendgoogleprovider.c250
-rw-r--r--src/goa/goabackendprovider.c68
-rw-r--r--src/goa/goabackendprovider.h27
9 files changed, 531 insertions, 24 deletions
diff --git a/data/dbus-interfaces.xml b/data/dbus-interfaces.xml
index b2517f5..0825b0a 100644
--- a/data/dbus-interfaces.xml
+++ b/data/dbus-interfaces.xml
@@ -108,7 +108,7 @@
account (for example <email>user@gmail.com</email>).
Objects implementing this interface also implement the
- #org.gnome.OnlineAccounts.Account interface.
+ #org.gnome.OnlineAccounts.Account and #org.gnome.OnlineAccounts.AccessTokenBased interfaces.
When creating this kind of account via the
org.gnome.OnlineAccounts.Manager.AddAccount() method, you need
@@ -116,7 +116,6 @@
the @details parameter.
-->
<interface name="org.gnome.OnlineAccounts.GoogleAccount">
-
<!-- EmailAddress: The email address identifying the account.
This value cannot be changed once set.
@@ -125,6 +124,29 @@
</interface>
<!--
+ org.gnome.OnlineAccounts.AccessTokenBased:
+
+ An account object implements this interface if it is based on
+ access tokens such as e.g. <ulink
+ url="http://en.wikipedia.org/wiki/OAuth">OAuth</ulink>.
+ -->
+ <interface name="org.gnome.OnlineAccounts.AccessTokenBased">
+ <!--
+ GetAccessToken:
+ @access_token: The access token that can be used to access online services.
+ @expires_in: The duration that the returned token is valid for, in seconds or 0 if unknown.
+
+ Use this method to obtains an access token to access services
+ for the account. Because networking is involved, this method may
+ take a very long time to complete.
+ -->
+ <method name="GetAccessToken">
+ <arg name="access_token" type="s" direction="out"/>
+ <arg name="expires_in" type="i" direction="out"/>
+ </method>
+ </interface>
+
+ <!--
org.gnome.OnlineAccounts.Manager:
An interface used for managing accounts.
diff --git a/doc/goa-docs.xml b/doc/goa-docs.xml
index b207bf7..4beae9f 100644
--- a/doc/goa-docs.xml
+++ b/doc/goa-docs.xml
@@ -101,6 +101,7 @@
<xi:include href="../src/goa/goa-generated-doc-org.gnome.OnlineAccounts.Manager.xml"/>
<xi:include href="../src/goa/goa-generated-doc-org.gnome.OnlineAccounts.Account.xml"/>
<xi:include href="../src/goa/goa-generated-doc-org.gnome.OnlineAccounts.GoogleAccount.xml"/>
+ <xi:include href="../src/goa/goa-generated-doc-org.gnome.OnlineAccounts.AccessTokenBased.xml"/>
</chapter>
</part>
@@ -113,6 +114,7 @@
<xi:include href="xml/GoaManager.xml"/>
<xi:include href="xml/GoaAccount.xml"/>
<xi:include href="xml/GoaGoogleAccount.xml"/>
+ <xi:include href="xml/GoaAccessTokenBased.xml"/>
</part>
<part id="ref-backend-library">
diff --git a/doc/goa-sections.txt b/doc/goa-sections.txt
index e70892b..a853657 100644
--- a/doc/goa-sections.txt
+++ b/doc/goa-sections.txt
@@ -54,9 +54,11 @@ GoaObjectIface
goa_object_get_manager
goa_object_get_account
goa_object_get_google_account
+goa_object_get_access_token_based
goa_object_peek_manager
goa_object_peek_account
goa_object_peek_google_account
+goa_object_peek_access_token_based
GoaObjectProxy
GoaObjectProxyClass
goa_object_proxy_new
@@ -66,6 +68,7 @@ goa_object_skeleton_new
goa_object_skeleton_set_manager
goa_object_skeleton_set_account
goa_object_skeleton_set_google_account
+goa_object_skeleton_set_access_token_based
<SUBSECTION Standard>
goa_object_get_type
goa_object_proxy_get_type
@@ -187,6 +190,50 @@ goa_google_account_skeleton_get_type
</SECTION>
<SECTION>
+<FILE>GoaAccessTokenBased</FILE>
+GoaAccessTokenBased
+GoaAccessTokenBasedIface
+goa_access_token_based_interface_info
+goa_access_token_based_call_get_access_token
+goa_access_token_based_call_get_access_token_finish
+goa_access_token_based_call_get_access_token_sync
+goa_access_token_based_complete_get_access_token
+GoaAccessTokenBasedProxy
+GoaAccessTokenBasedProxyClass
+goa_access_token_based_proxy_new
+goa_access_token_based_proxy_new_finish
+goa_access_token_based_proxy_new_sync
+goa_access_token_based_proxy_new_for_bus
+goa_access_token_based_proxy_new_for_bus_finish
+goa_access_token_based_proxy_new_for_bus_sync
+GoaAccessTokenBasedSkeleton
+GoaAccessTokenBasedSkeletonClass
+goa_access_token_based_skeleton_new
+<SUBSECTION Standard>
+GOA_ACCESS_TOKEN_BASED
+GOA_ACCESS_TOKEN_BASED_GET_IFACE
+GOA_ACCESS_TOKEN_BASED_PROXY
+GOA_ACCESS_TOKEN_BASED_PROXY_CLASS
+GOA_ACCESS_TOKEN_BASED_PROXY_GET_CLASS
+GOA_ACCESS_TOKEN_BASED_SKELETON
+GOA_ACCESS_TOKEN_BASED_SKELETON_CLASS
+GOA_ACCESS_TOKEN_BASED_SKELETON_GET_CLASS
+GOA_IS_ACCESS_TOKEN_BASED
+GOA_IS_ACCESS_TOKEN_BASED_PROXY
+GOA_IS_ACCESS_TOKEN_BASED_PROXY_CLASS
+GOA_IS_ACCESS_TOKEN_BASED_SKELETON
+GOA_IS_ACCESS_TOKEN_BASED_SKELETON_CLASS
+GOA_TYPE_ACCESS_TOKEN_BASED
+GOA_TYPE_ACCESS_TOKEN_BASED_PROXY
+GOA_TYPE_ACCESS_TOKEN_BASED_SKELETON
+GoaAccessTokenBasedProxyPrivate
+GoaAccessTokenBasedSkeletonPrivate
+goa_access_token_based_get_type
+goa_access_token_based_proxy_get_type
+goa_access_token_based_skeleton_get_type
+</SECTION>
+
+<SECTION>
<FILE>GoaObjectManagerClient</FILE>
GoaObjectManagerClient
GoaObjectManagerClientClass
@@ -242,6 +289,9 @@ GoaBackendProviderClass
goa_backend_provider_get_provider_type
goa_backend_provider_get_name
goa_backend_provider_add_account
+goa_backend_provider_get_access_token_supported
+goa_backend_provider_get_access_token
+goa_backend_provider_get_access_token_finish
GOA_BACKEND_PROVIDER_EXTENSION_POINT_NAME
goa_backend_provider_get_all
goa_backend_provider_get_for_provider_type
diff --git a/doc/goa.types b/doc/goa.types
index f78f3e4..3075ebd 100644
--- a/doc/goa.types
+++ b/doc/goa.types
@@ -14,3 +14,4 @@ goa_object_manager_client_get_type
goa_object_get_type
goa_object_proxy_get_type
goa_object_skeleton_get_type
+goa_access_token_based_get_type
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index 9665665..3d60bc3 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -12,6 +12,7 @@ INCLUDES = \
-DPACKAGE_LIB_DIR=\""$(libdir)"\" \
-D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT \
-DGOA_API_IS_SUBJECT_TO_CHANGE \
+ -DGOA_BACKEND_API_IS_SUBJECT_TO_CHANGE \
$(WARN_CFLAGS) \
$(NULL)
@@ -26,11 +27,14 @@ goa_daemon_SOURCES = \
goa_daemon_CFLAGS = \
$(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
$(NULL)
goa_daemon_LDADD = \
$(GLIB_LIBS) \
$(top_builddir)/src/goa/libgoa.la \
+ $(top_builddir)/src/goa/libgoa-backend.la \
+ $(GTK_LIBS) \
$(NULL)
clean-local :
diff --git a/src/daemon/goadaemon.c b/src/daemon/goadaemon.c
index eb90aa4..2539f87 100644
--- a/src/daemon/goadaemon.c
+++ b/src/daemon/goadaemon.c
@@ -32,6 +32,7 @@
#include <glib/gi18n.h>
#include "goadaemon.h"
+#include "goa/goabackend.h"
struct _GoaDaemon
{
@@ -66,6 +67,10 @@ static gboolean on_add_account (GoaManager *object,
GVariant *details,
gpointer user_data);
+static gboolean on_handle_get_access_token (GoaAccessTokenBased *object,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data);
+
static void goa_daemon_reload_configuration (GoaDaemon *daemon);
G_DEFINE_TYPE (GoaDaemon, goa_daemon, G_TYPE_OBJECT);
@@ -373,8 +378,10 @@ update_account_object (GoaDaemon *daemon,
gboolean just_added)
{
GoaAccount *account;
+ GoaAccessTokenBased *access_token_based;
gboolean ret;
- gchar *s;
+ gchar *name;
+ gchar *type;
g_return_val_if_fail (GOA_IS_DAEMON (daemon), FALSE);
g_return_val_if_fail (G_IS_DBUS_OBJECT_SKELETON (object), FALSE);
@@ -382,48 +389,73 @@ update_account_object (GoaDaemon *daemon,
g_return_val_if_fail (key_file != NULL, FALSE);
ret = FALSE;
+ name = NULL;
+ type = NULL;
+ account = NULL;
+ access_token_based = NULL;
g_debug ("updating %s %d", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), just_added);
+ type = g_key_file_get_string (key_file, group, "Type", NULL);
+ name = g_key_file_get_string (key_file, group, "Name", NULL);
if (just_added)
{
+ GoaBackendProvider *provider;
+
account = goa_account_skeleton_new ();
goa_object_skeleton_set_account (object, account);
+
+ provider = goa_backend_provider_get_for_provider_type (type);
+ if (provider == NULL)
+ {
+ /* TODO: syslog */
+ g_warning ("Unsupported account type %s for id %s (no provider)", type, goa_account_get_id (account));
+ goto out;
+ }
+ if (goa_backend_provider_get_access_token_supported (provider))
+ {
+ access_token_based = goa_access_token_based_skeleton_new ();
+ g_signal_connect (access_token_based,
+ "handle-get-access-token",
+ G_CALLBACK (on_handle_get_access_token),
+ daemon);
+ goa_object_skeleton_set_access_token_based (object, access_token_based);
+ }
+ g_object_unref (provider);
}
else
{
account = goa_object_get_account (GOA_OBJECT (object));
+ access_token_based = goa_object_get_access_token_based (GOA_OBJECT (object));
}
goa_account_set_id (account, g_strrstr (g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), "/") + 1);
- s = g_key_file_get_string (key_file, group, "Name", NULL);
- goa_account_set_name (account, s);
- g_free (s);
+ goa_account_set_name (account, type);
+ goa_account_set_account_type (account, type);
- s = g_key_file_get_string (key_file, group, "Type", NULL);
- goa_account_set_account_type (account, s);
/* TODO: some kind of GoaAccountProvider subclass stuff */
- if (g_strcmp0 (s, "google") == 0)
+ if (g_strcmp0 (type, "google") == 0)
{
if (!update_account_object_google (daemon, object, group, key_file, just_added))
{
- g_free (s);
goto out;
}
}
else
{
/* TODO: syslog */
- g_warning ("Unsupported account type %s for id %s", s, goa_account_get_id (account));
- g_free (s);
+ g_warning ("Unsupported account type %s for id %s", type, goa_account_get_id (account));
goto out;
}
- g_free (s);
ret = TRUE;
out:
g_object_unref (account);
+ if (access_token_based != NULL)
+ g_object_unref (access_token_based);
+ g_free (type);
+ g_free (name);
return ret;
}
@@ -748,3 +780,76 @@ on_add_account (GoaManager *manager,
return TRUE; /* invocation was handled */
}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GoaDaemon *daemon;
+ GoaObject *object;
+ GDBusMethodInvocation *invocation;
+} AccessTokenData;
+
+static void
+access_token_data_free (AccessTokenData *data)
+{
+ g_object_unref (data->daemon);
+ g_object_unref (data->object);
+ g_free (data);
+}
+
+static void
+get_access_token_cb (GoaBackendProvider *provider,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ AccessTokenData *data = user_data;
+ GError *error;
+ gchar *access_token;
+ gint expires_in;
+
+ error = NULL;
+ access_token = goa_backend_provider_get_access_token_finish (provider, &expires_in, res, &error);
+ if (access_token == NULL)
+ {
+ g_dbus_method_invocation_return_gerror (data->invocation, error);
+ g_error_free (error);
+ }
+ else
+ {
+ goa_access_token_based_complete_get_access_token (goa_object_peek_access_token_based (data->object),
+ data->invocation,
+ access_token,
+ expires_in);
+ g_free (access_token);
+ }
+ access_token_data_free (data);
+}
+
+static gboolean
+on_handle_get_access_token (GoaAccessTokenBased *instance,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ GoaDaemon *daemon = GOA_DAEMON (user_data);
+ GoaObject *object;
+ GoaAccount *account;
+ GoaBackendProvider *provider;
+ AccessTokenData *data;
+
+ object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (instance)));
+ account = goa_object_get_account (object);
+ provider = goa_backend_provider_get_for_provider_type (goa_account_get_account_type (account));
+
+ data = g_new0 (AccessTokenData, 1);
+ data->daemon = g_object_ref (daemon);
+ data->object = g_object_ref (object);
+ data->invocation = invocation;
+ goa_backend_provider_get_access_token (provider,
+ object,
+ NULL, /* GCancellable* */
+ (GAsyncReadyCallback) get_access_token_cb,
+ data);
+
+ return TRUE; /* invocation was handled */
+}
diff --git a/src/goa/goabackendgoogleprovider.c b/src/goa/goabackendgoogleprovider.c
index a94a23e..c53136d 100644
--- a/src/goa/goabackendgoogleprovider.c
+++ b/src/goa/goabackendgoogleprovider.c
@@ -74,6 +74,16 @@ static GoaObject *goa_backend_google_provider_add_account (GoaBackendPro
GtkBox *vbox,
GError **error);
+static gboolean goa_backend_google_provider_get_access_token_supported (GoaBackendProvider *provider);
+static void goa_backend_google_provider_get_access_token (GoaBackendProvider *provider,
+ GoaObject *object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static gchar *goa_backend_google_provider_get_access_token_finish (GoaBackendProvider *provider,
+ gint *out_expires_in,
+ GAsyncResult *res,
+ GError **error);
/**
* SECTION:goabackendgoogleprovider
@@ -100,9 +110,12 @@ goa_backend_google_provider_class_init (GoaBackendGoogleProviderClass *klass)
GoaBackendProviderClass *provider_klass;
provider_klass = GOA_BACKEND_PROVIDER_CLASS (klass);
- provider_klass->get_provider_type = goa_backend_google_provider_get_provider_type;
- provider_klass->get_name = goa_backend_google_provider_get_name;
- provider_klass->add_account = goa_backend_google_provider_add_account;
+ provider_klass->get_provider_type = goa_backend_google_provider_get_provider_type;
+ provider_klass->get_name = goa_backend_google_provider_get_name;
+ provider_klass->add_account = goa_backend_google_provider_add_account;
+ provider_klass->get_access_token_supported = goa_backend_google_provider_get_access_token_supported;
+ provider_klass->get_access_token = goa_backend_google_provider_get_access_token;
+ provider_klass->get_access_token_finish = goa_backend_google_provider_get_access_token_finish;
}
static const gchar *
@@ -147,9 +160,10 @@ on_web_view_notify_title (GObject *object,
}
static gchar *
-get_access_token_sync (SoupSession *session,
- const gchar *authorization_code,
- GError **error)
+get_access_and_refresh_token_sync (SoupSession *session,
+ const gchar *authorization_code,
+ gchar **out_refresh_token,
+ GError **error)
{
SoupMessage *message;
gint code;
@@ -158,6 +172,7 @@ get_access_token_sync (SoupSession *session,
SoupBuffer *buffer;
JsonParser *parser;
JsonObject *json_object;
+ const gchar *refresh_token;
g_return_val_if_fail (authorization_code != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
@@ -197,6 +212,15 @@ get_access_token_sync (SoupSession *session,
goto out;
}
json_object = json_node_get_object (json_parser_get_root (parser));
+ refresh_token = json_object_get_string_member (json_object, "refresh_token");
+ if (refresh_token == NULL)
+ {
+ g_set_error (error,
+ GOA_ERROR,
+ GOA_ERROR_FAILED,
+ _("Didn't find refresh_token in JSON data"));
+ goto out;
+ }
ret = g_strdup (json_object_get_string_member (json_object, "access_token"));
if (ret == NULL)
{
@@ -207,6 +231,9 @@ get_access_token_sync (SoupSession *session,
goto out;
}
+ if (out_refresh_token != NULL)
+ *out_refresh_token = g_strdup (refresh_token);
+
out:
if (parser != NULL)
g_object_unref (parser);
@@ -339,6 +366,7 @@ goa_backend_google_provider_add_account (GoaBackendProvider *_provider,
gint response;
AddData data;
gchar *access_token;
+ gchar *refresh_token;
gchar *email_address;
GList *accounts;
GList *l;
@@ -353,6 +381,7 @@ goa_backend_google_provider_add_account (GoaBackendProvider *_provider,
ret = NULL;
session = NULL;
access_token = NULL;
+ refresh_token = NULL;
email_address = NULL;
accounts = NULL;
password_description = NULL;
@@ -425,10 +454,11 @@ goa_backend_google_provider_add_account (GoaBackendProvider *_provider,
/* OK, we now have the authorization code... now we need to get the
* email address (to e.g. check if the account already exists on
- * @client).. for that we need to get a (short-lived) access token..
+ * @client).. for that we need to get a (short-lived) access token
+ * and a refresh_token
*/
session = soup_session_sync_new ();
- access_token = get_access_token_sync (session, data.authorization_code, error);
+ access_token = get_access_and_refresh_token_sync (session, data.authorization_code, &refresh_token, error);
if (access_token == NULL)
goto out;
@@ -475,11 +505,11 @@ goa_backend_google_provider_add_account (GoaBackendProvider *_provider,
if (data.error != NULL)
goto out;
- password_description = g_strdup_printf (_("Google OAuth2 authorization code for %s"), email_address);
+ password_description = g_strdup_printf (_("Google OAuth2 refresh code for %s"), email_address);
gnome_keyring_store_password (&keyring_password_schema,
NULL, /* default keyring */
password_description,
- data.authorization_code,
+ refresh_token,
store_password_cb,
&data,
NULL, /* GDestroyNotify */
@@ -519,6 +549,206 @@ goa_backend_google_provider_add_account (GoaBackendProvider *_provider,
if (data.loop != NULL)
g_main_loop_unref (data.loop);
g_free (access_token);
+ g_free (refresh_token);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+goa_backend_google_provider_get_access_token_supported (GoaBackendProvider *provider)
+{
+ return TRUE;
+}
+
+typedef struct
+{
+ volatile gint ref_count;
+
+ GoaObject *object;
+ GSimpleAsyncResult *simple;
+ SoupSession *session;
+ SoupMessage *message;
+
+ gchar *access_token;
+ gint expires_in;
+} GetAccessTokenData;
+
+static void
+get_access_token_data_unref (GetAccessTokenData *data)
+{
+ if (g_atomic_int_dec_and_test (&data->ref_count))
+ {
+ g_object_unref (data->object);
+ g_object_unref (data->simple);
+ g_object_unref (data->session);
+ g_object_unref (data->message);
+ g_free (data->access_token);
+ g_slice_free (GetAccessTokenData, data);
+ }
+}
+
+static GetAccessTokenData *
+get_access_token_data_ref (GetAccessTokenData *data)
+{
+ g_atomic_int_inc (&data->ref_count);
+ return data;
+}
+
+static void
+get_access_token_cb (SoupSession *session,
+ SoupMessage *message,
+ gpointer user_data)
+{
+ GetAccessTokenData *data = user_data;
+ SoupBuffer *buffer;
+ JsonParser *parser;
+ JsonObject *json_object;
+ GError *error;
+
+ buffer = NULL;
+ parser = NULL;
+
+ if (message->status_code != SOUP_STATUS_OK)
+ {
+ g_simple_async_result_set_error (data->simple,
+ GOA_ERROR,
+ GOA_ERROR_FAILED,
+ _("Expected status 200 when requesting access token, instead got status %d (%s)"),
+ message->status_code,
+ message->reason_phrase);
+ goto out;
+ }
+
+ buffer = soup_message_body_flatten (message->response_body);
+ parser = json_parser_new ();
+ error = NULL;
+ if (!json_parser_load_from_data (parser, buffer->data, buffer->length, &error))
+ {
+ g_prefix_error (&error, _("Error parsing response as JSON: "));
+ g_simple_async_result_take_error (data->simple, error);
+ goto out;
+ }
+ json_object = json_node_get_object (json_parser_get_root (parser));
+ data->access_token = g_strdup (json_object_get_string_member (json_object, "access_token"));
+ if (data->access_token == NULL)
+ {
+ g_simple_async_result_set_error (data->simple,
+ GOA_ERROR,
+ GOA_ERROR_FAILED,
+ _("Didn't find access_token in JSON data"));
+ goto out;
+ }
+
+ data->expires_in = 0;
+ if (json_object_has_member (json_object, "expires_in"))
+ data->expires_in = json_object_get_int_member (json_object, "expires_in");
+
+ g_simple_async_result_set_op_res_gpointer (data->simple,
+ get_access_token_data_ref (data),
+ (GDestroyNotify) get_access_token_data_unref);
+
+ out:
+ if (parser != NULL)
+ g_object_unref (parser);
+ if (buffer != NULL)
+ soup_buffer_free (buffer);
+ g_simple_async_result_complete_in_idle (data->simple);
+ get_access_token_data_unref (data);
+}
+
+static void
+find_password_cb (GnomeKeyringResult result,
+ const gchar *refresh_token,
+ gpointer user_data)
+{
+ GetAccessTokenData *data = user_data;
+ gchar *body;
+
+ if (result != GNOME_KEYRING_RESULT_OK)
+ {
+ g_simple_async_result_set_error (data->simple,
+ GOA_ERROR,
+ GOA_ERROR_FAILED,
+ _("Failed to retrieve the authorization code from the keyring: %s"),
+ gnome_keyring_result_to_message (result));
+ g_simple_async_result_complete_in_idle (data->simple);
+ get_access_token_data_unref (data);
+ goto out;
+ }
+
+ /* Swell. Now get the access code as per http://code.google.com/apis/accounts/docs/OAuth2.html */
+ data->message = soup_message_new ("POST", "https://accounts.google.com/o/oauth2/token");
+ soup_message_headers_append (data->message->request_headers, "Content-Type", "application/x-www-form-urlencoded");
+ body = g_strdup_printf ("client_id=%s&"
+ "client_secret=%s&"
+ "refresh_token=%s&"
+ "grant_type=refresh_token",
+ GOOGLE_CLIENT_ID,
+ GOOGLE_CLIENT_SECRET,
+ refresh_token);
+ g_debug ("body=%s", body);
+ soup_message_body_append (data->message->request_body, SOUP_MEMORY_TAKE, body, strlen (body));
+ soup_session_queue_message (data->session,
+ g_object_ref (data->message),
+ get_access_token_cb,
+ data);
+
+ out:
+ ;
+}
+
+static void
+goa_backend_google_provider_get_access_token (GoaBackendProvider *provider,
+ GoaObject *object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GetAccessTokenData *data;
+ const gchar *email_address;
+
+ data = g_slice_new0 (GetAccessTokenData);
+ data->ref_count = 1;
+ data->object = g_object_ref (object);
+ data->session = soup_session_async_new ();
+ data->simple = g_simple_async_result_new (G_OBJECT (provider),
+ callback,
+ user_data,
+ goa_backend_google_provider_get_access_token);
+
+ email_address = goa_google_account_get_email_address (goa_object_peek_google_account (object));
+
+ /* First, get the authorization code from gnome-keyring */
+ gnome_keyring_find_password (&keyring_password_schema,
+ find_password_cb,
+ data,
+ NULL, /* GDestroyNotify */
+ "goa-google-email-address", email_address,
+ NULL);
+}
+
+static gchar *
+goa_backend_google_provider_get_access_token_finish (GoaBackendProvider *provider,
+ gint *out_expires_in,
+ GAsyncResult *res,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ GetAccessTokenData *data;
+ gchar *ret;
+
+ g_warn_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (provider), goa_backend_google_provider_get_access_token));
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ data = g_simple_async_result_get_op_res_gpointer (simple);
+
+ ret = g_strdup (data->access_token);
+ if (out_expires_in != NULL)
+ *out_expires_in = data->expires_in;
+
return ret;
}
diff --git a/src/goa/goabackendprovider.c b/src/goa/goabackendprovider.c
index 34ad433..3086829 100644
--- a/src/goa/goabackendprovider.c
+++ b/src/goa/goabackendprovider.c
@@ -136,6 +136,74 @@ goa_backend_provider_add_account (GoaBackendProvider *provider,
/* ---------------------------------------------------------------------------------------------------- */
+/**
+ * goa_backend_provider_get_access_token_supported:
+ * @provider: A #GoaBackendProvider.
+ *
+ * Function to check if @provider is access token based.
+ *
+ * Returns: %TRUE if access token based, %FALSE otherwise.
+ */
+gboolean
+goa_backend_provider_get_access_token_supported (GoaBackendProvider *provider)
+{
+ g_return_val_if_fail (GOA_IS_BACKEND_PROVIDER (provider), FALSE);
+ return GOA_BACKEND_PROVIDER_GET_CLASS (provider)->get_access_token_supported (provider);
+}
+
+/**
+ * goa_backend_provider_get_access_token:
+ * @provider: A #GoaBackendProvider.
+ * @object: The #GoaObject to get an access token for.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: The function to call when the request is satisfied.
+ * @user_data: Pointer to pass to @callback.
+ *
+ * Starts obtaining an access token for the account identified by @object.
+ *
+ * When the access token is ready, @callback is invoked in the <link
+ * linkend="g-main-context-push-thread-default">thread-default main
+ * loop</link> this function was called from.
+ */
+void
+goa_backend_provider_get_access_token (GoaBackendProvider *provider,
+ GoaObject *object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (GOA_IS_BACKEND_PROVIDER (provider));
+ g_return_if_fail (GOA_IS_OBJECT (object));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ GOA_BACKEND_PROVIDER_GET_CLASS (provider)->get_access_token (provider, object, cancellable, callback, user_data);
+}
+
+/**
+ * goa_backend_provider_get_access_token_finish:
+ * @provider: A #GoaBackendProvider.
+ * @out_expires_in: (out): Return location for expiration time (in seconds), or %NULL to ignore.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to goa_backend_provider_get_access_token().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with goa_backend_provider_get_access_token().
+ *
+ * Returns: (transfer full): The access token or %NULL if @error is set.
+ */
+gchar *
+goa_backend_provider_get_access_token_finish (GoaBackendProvider *provider,
+ gint *out_expires_in,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (GOA_IS_BACKEND_PROVIDER (provider), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ return GOA_BACKEND_PROVIDER_GET_CLASS (provider)->get_access_token_finish (provider, out_expires_in, res, error);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
ensure_ep_and_builtins (void)
{
diff --git a/src/goa/goabackendprovider.h b/src/goa/goabackendprovider.h
index 751b86b..91914db 100644
--- a/src/goa/goabackendprovider.h
+++ b/src/goa/goabackendprovider.h
@@ -59,8 +59,10 @@ struct _GoaBackendProvider
* @parent_class: The parent class.
* @get_provider_type: Virtual function for goa_backend_provider_get_provider_type().
* @get_name: Virtual function for goa_backend_provider_get_name().
- * @add_account_interactive: Virtual function for goa_backend_provider_add_account_interactive().
* @add_account: Virtual function for goa_backend_provider_add_account().
+ * @get_access_token_supported: Virtual function for goa_backend_provider_get_access_token_supported().
+ * @get_access_token: Virtual function for goa_backend_provider_get_access_token().
+ * @get_access_token_finish: Virtual function for goa_backend_provider_get_access_token_finish().
*
* Class structure for #GoaBackendProvider.
*/
@@ -76,6 +78,18 @@ struct _GoaBackendProviderClass
GtkBox *vbox,
GError **error);
+ /* AccessTokenBased support */
+ gboolean (*get_access_token_supported) (GoaBackendProvider *provider);
+ void (*get_access_token) (GoaBackendProvider *provider,
+ GoaObject *object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gchar *(*get_access_token_finish) (GoaBackendProvider *provider,
+ gint *out_expires_in,
+ GAsyncResult *res,
+ GError **error);
+
/*< private >*/
/* Padding for future expansion */
gpointer goa_reserved[32];
@@ -90,6 +104,17 @@ GoaObject *goa_backend_provider_add_account (GoaBackendProvider
GtkBox *vbox,
GError **error);
+gboolean goa_backend_provider_get_access_token_supported (GoaBackendProvider *provider);
+void goa_backend_provider_get_access_token (GoaBackendProvider *provider,
+ GoaObject *object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *goa_backend_provider_get_access_token_finish (GoaBackendProvider *provider,
+ gint *out_expires_in,
+ GAsyncResult *res,
+ GError **error);
+
/**
* GOA_BACKEND_PROVIDER_EXTENSION_POINT_NAME:
*