diff options
author | David Zeuthen <davidz@redhat.com> | 2011-04-25 17:36:42 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2011-04-25 17:36:42 -0400 |
commit | 0ce85dce3d531db24961467af518de3dc47b9a82 (patch) | |
tree | ebecb862395ef1ad7b6d0053921a45ceec2d3112 | |
parent | a789d5b5e8338939ffc45ebf80ea257cb00f6caa (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.xml | 26 | ||||
-rw-r--r-- | doc/goa-docs.xml | 2 | ||||
-rw-r--r-- | doc/goa-sections.txt | 50 | ||||
-rw-r--r-- | doc/goa.types | 1 | ||||
-rw-r--r-- | src/daemon/Makefile.am | 4 | ||||
-rw-r--r-- | src/daemon/goadaemon.c | 127 | ||||
-rw-r--r-- | src/goa/goabackendgoogleprovider.c | 250 | ||||
-rw-r--r-- | src/goa/goabackendprovider.c | 68 | ||||
-rw-r--r-- | src/goa/goabackendprovider.h | 27 |
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: * |