4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
19 #include "uoa-utils.h"
22 #include <glib/gi18n-lib.h>
23 #include <rest/rest-proxy.h>
24 #include <json-glib/json-glib.h>
25 #include <libsignon-glib/signon-glib.h>
27 #define GOOGLE_USERINFO_URI \
28 "https://www.googleapis.com/oauth2/v2/userinfo"
30 typedef struct _AsyncContext AsyncContext;
32 struct _AsyncContext {
33 GCancellable *cancellable;
39 async_context_free (AsyncContext *async_context)
41 if (async_context->cancellable != NULL)
42 g_object_unref (async_context->cancellable);
44 g_free (async_context->user_identity);
45 g_free (async_context->email_address);
47 g_slice_free (AsyncContext, async_context);
50 /****************************** Google Provider ******************************/
53 e_ag_account_google_got_userinfo_cb (RestProxyCall *call,
58 GSimpleAsyncResult *simple;
59 AsyncContext *async_context;
60 JsonParser *json_parser;
61 JsonObject *json_object;
64 GError *local_error = NULL;
66 simple = G_SIMPLE_ASYNC_RESULT (user_data);
67 async_context = g_simple_async_result_get_op_res_gpointer (simple);
70 g_simple_async_result_set_from_error (simple, error);
74 /* This is shamelessly stolen from goagoogleprovider.c */
76 if (rest_proxy_call_get_status_code (call) != 200) {
77 g_simple_async_result_set_error (
78 simple, G_IO_ERROR, G_IO_ERROR_FAILED,
79 _("Expected status 200 when requesting guid, "
80 "instead got status %d (%s)"),
81 rest_proxy_call_get_status_code (call),
82 rest_proxy_call_get_status_message (call));
86 json_parser = json_parser_new ();
87 json_parser_load_from_data (
89 rest_proxy_call_get_payload (call),
90 rest_proxy_call_get_payload_length (call),
93 if (local_error != NULL) {
96 _("Error parsing response as JSON: "));
97 g_simple_async_result_take_error (simple, local_error);
98 g_object_unref (json_parser);
102 json_node = json_parser_get_root (json_parser);
103 json_object = json_node_get_object (json_node);
104 email = json_object_get_string_member (json_object, "email");
107 async_context->user_identity = g_strdup (email);
108 async_context->email_address = g_strdup (email);
110 g_simple_async_result_set_error (
111 simple, G_IO_ERROR, G_IO_ERROR_FAILED,
112 _("Didn't find email member in JSON data"));
115 g_object_unref (json_parser);
118 g_simple_async_result_complete (simple);
120 g_object_unref (simple);
124 e_ag_account_google_session_process_cb (GObject *source_object,
125 GAsyncResult *result,
128 GSimpleAsyncResult *simple;
129 GVariant *session_data;
130 GError *error = NULL;
132 simple = G_SIMPLE_ASYNC_RESULT (user_data);
134 session_data = signon_auth_session_process_finish (
135 SIGNON_AUTH_SESSION (source_object), result, &error);
139 ((session_data != NULL) && (error == NULL)) ||
140 ((session_data == NULL) && (error != NULL)));
142 /* Use the access token to obtain the user's email address. */
144 if (session_data != NULL) {
147 gchar *access_token = NULL;
150 session_data, "AccessToken", "s", &access_token);
152 g_variant_unref (session_data);
154 proxy = rest_proxy_new (GOOGLE_USERINFO_URI, FALSE);
155 call = rest_proxy_new_call (proxy);
156 rest_proxy_call_set_method (call, "GET");
158 /* XXX This should never be NULL, but if it is just let
159 * the call fail and pick up the resulting GError. */
160 if (access_token != NULL) {
161 rest_proxy_call_add_param (
162 call, "access_token", access_token);
163 g_free (access_token);
166 /* XXX The 3rd argument is supposed to be a GObject
167 * that RestProxyCall weakly references such that
168 * its disposal cancels the call. This obviously
169 * predates GCancellable. Too bizarre to bother. */
170 rest_proxy_call_async (
171 call, e_ag_account_google_got_userinfo_cb,
172 NULL, g_object_ref (simple), &error);
175 /* Undo the reference added to the async call. */
176 g_object_unref (simple);
179 g_object_unref (proxy);
180 g_object_unref (call);
184 g_simple_async_result_take_error (simple, error);
185 g_simple_async_result_complete (simple);
188 g_object_unref (simple);
192 e_ag_account_collect_google_userinfo (GSimpleAsyncResult *simple,
193 AgAccount *ag_account,
194 GCancellable *cancellable)
196 AgAccountService *ag_account_service = NULL;
197 SignonAuthSession *session;
198 AgAuthData *ag_auth_data;
200 GError *error = NULL;
202 /* First obtain an OAuth 2.0 access token. */
204 list = ag_account_list_services_by_type (ag_account, "mail");
206 ag_account_service = ag_account_service_new (
207 ag_account, (AgService *) list->data);
208 ag_service_list_free (list);
211 g_return_if_fail (ag_account_service != NULL);
213 ag_auth_data = ag_account_service_get_auth_data (ag_account_service);
215 session = signon_auth_session_new (
216 ag_auth_data_get_credentials_id (ag_auth_data),
217 ag_auth_data_get_method (ag_auth_data), &error);
221 ((session != NULL) && (error == NULL)) ||
222 ((session == NULL) && (error != NULL)));
224 if (session != NULL) {
225 signon_auth_session_process_async (
227 ag_auth_data_get_login_parameters (ag_auth_data, NULL),
228 ag_auth_data_get_mechanism (ag_auth_data),
230 e_ag_account_google_session_process_cb,
231 g_object_ref (simple));
233 g_simple_async_result_take_error (simple, error);
234 g_simple_async_result_complete_in_idle (simple);
237 ag_auth_data_unref (ag_auth_data);
239 g_object_unref (ag_account_service);
240 g_object_unref (simple);
243 /****************************** Yahoo! Provider ******************************/
246 e_ag_account_collect_yahoo_userinfo (GSimpleAsyncResult *simple,
247 AgAccount *ag_account,
248 GCancellable *cancellable)
250 AsyncContext *async_context;
251 GString *email_address;
252 const gchar *display_name;
254 /* XXX This is a bit of a hack just to get *something* working
255 * for Yahoo! accounts. The proper way to obtain userinfo
256 * for Yahoo! is through OAuth 1.0 and OpenID APIs, which
257 * does not look trivial. This will do for now. */
259 /* XXX AgAccount's display name also sort of doubles as a user
260 * name, which may or may not be an email address. If the
261 * display name has no domain part, assume "@yahoo.com". */
263 display_name = ag_account_get_display_name (ag_account);
264 g_return_if_fail (display_name != NULL);
266 email_address = g_string_new (display_name);
267 if (strchr (email_address->str, '@') == NULL)
268 g_string_append (email_address, "@yahoo.com");
270 async_context = g_simple_async_result_get_op_res_gpointer (simple);
272 async_context->user_identity = g_strdup (email_address->str);
273 async_context->email_address = g_strdup (email_address->str);
275 g_simple_async_result_complete_in_idle (simple);
277 g_object_unref (simple);
280 /************************ End Provider-Specific Code *************************/
283 e_ag_account_collect_userinfo (AgAccount *ag_account,
284 GCancellable *cancellable,
285 GAsyncReadyCallback callback,
288 GSimpleAsyncResult *simple;
289 AsyncContext *async_context;
290 const gchar *provider_name;
292 g_return_if_fail (AG_IS_ACCOUNT (ag_account));
294 provider_name = ag_account_get_provider_name (ag_account);
295 g_return_if_fail (provider_name != NULL);
297 async_context = g_slice_new0 (AsyncContext);
299 if (G_IS_CANCELLABLE (cancellable))
300 async_context->cancellable = g_object_ref (cancellable);
302 simple = g_simple_async_result_new (
303 G_OBJECT (ag_account), callback,
304 user_data, e_ag_account_collect_userinfo);
306 g_simple_async_result_set_check_cancellable (simple, cancellable);
308 g_simple_async_result_set_op_res_gpointer (
309 simple, async_context, (GDestroyNotify) async_context_free);
311 /* XXX This has to be done differently for each provider. */
313 if (g_str_equal (provider_name, "google")) {
314 e_ag_account_collect_google_userinfo (
315 g_object_ref (simple), ag_account, cancellable);
316 } else if (g_str_equal (provider_name, "yahoo")) {
317 e_ag_account_collect_yahoo_userinfo (
318 g_object_ref (simple), ag_account, cancellable);
320 g_warn_if_reached ();
321 g_simple_async_result_complete_in_idle (simple);
324 g_object_unref (simple);
328 e_ag_account_collect_userinfo_finish (AgAccount *ag_account,
329 GAsyncResult *result,
330 gchar **out_user_identity,
331 gchar **out_email_address,
334 GSimpleAsyncResult *simple;
335 AsyncContext *async_context;
337 g_return_val_if_fail (
338 g_simple_async_result_is_valid (
339 result, G_OBJECT (ag_account),
340 e_ag_account_collect_userinfo), FALSE);
342 simple = G_SIMPLE_ASYNC_RESULT (result);
343 async_context = g_simple_async_result_get_op_res_gpointer (simple);
345 if (g_simple_async_result_propagate_error (simple, error))
348 /* The result strings may be NULL without an error. */
350 if (out_user_identity != NULL) {
351 *out_user_identity = async_context->user_identity;
352 async_context->user_identity = NULL;
355 if (out_email_address != NULL) {
356 *out_email_address = async_context->email_address;
357 async_context->email_address = NULL;