Replace crap desktop notifications with crap GTK+ dialogs
[empathy-mirror.git] / libempathy / empathy-goa-auth-handler.c
blob37973000481de96f80ab35505ee5e0eaf2094a6e
1 /*
2 * empathy-goa-auth-handler.c - Source for Goa SASL authentication
3 * Copyright (C) 2011 Collabora Ltd.
4 * @author Xavier Claessens <xavier.claessens@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "config.h"
22 #include "empathy-goa-auth-handler.h"
24 #define GOA_API_IS_SUBJECT_TO_CHANGE /* awesome! */
25 #include <goa/goa.h>
27 #include "empathy-sasl-mechanisms.h"
29 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
30 #include "empathy-debug.h"
32 struct _EmpathyGoaAuthHandlerPriv
34 GoaClient *client;
35 gboolean client_preparing;
37 /* List of AuthData waiting for client to be created */
38 GList *auth_queue;
41 G_DEFINE_TYPE (EmpathyGoaAuthHandler, empathy_goa_auth_handler, G_TYPE_OBJECT);
43 static void
44 empathy_goa_auth_handler_init (EmpathyGoaAuthHandler *self)
46 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
47 EMPATHY_TYPE_GOA_AUTH_HANDLER, EmpathyGoaAuthHandlerPriv);
50 static void
51 empathy_goa_auth_handler_dispose (GObject *object)
53 EmpathyGoaAuthHandler *self = (EmpathyGoaAuthHandler *) object;
55 /* AuthData keeps a ref on self */
56 g_assert (self->priv->auth_queue == NULL);
58 tp_clear_object (&self->priv->client);
60 G_OBJECT_CLASS (empathy_goa_auth_handler_parent_class)->dispose (object);
63 static void
64 empathy_goa_auth_handler_class_init (EmpathyGoaAuthHandlerClass *klass)
66 GObjectClass *oclass = G_OBJECT_CLASS (klass);
68 oclass->dispose = empathy_goa_auth_handler_dispose;
70 g_type_class_add_private (klass, sizeof (EmpathyGoaAuthHandlerPriv));
73 EmpathyGoaAuthHandler *
74 empathy_goa_auth_handler_new (void)
76 return g_object_new (EMPATHY_TYPE_GOA_AUTH_HANDLER, NULL);
79 typedef struct
81 EmpathyGoaAuthHandler *self;
82 TpChannel *channel;
83 TpAccount *account;
85 GoaObject *goa_object;
86 gchar *access_token;
87 } AuthData;
89 static AuthData *
90 auth_data_new (EmpathyGoaAuthHandler *self,
91 TpChannel *channel,
92 TpAccount *account)
94 AuthData *data;
96 data = g_slice_new0 (AuthData);
97 data->self = g_object_ref (self);
98 data->channel = g_object_ref (channel);
99 data->account = g_object_ref (account);
101 return data;
104 static void
105 auth_data_free (AuthData *data)
107 tp_clear_object (&data->self);
108 tp_clear_object (&data->channel);
109 tp_clear_object (&data->account);
110 tp_clear_object (&data->goa_object);
111 g_free (data->access_token);
112 g_slice_free (AuthData, data);
115 static void
116 fail_auth (AuthData *data)
118 DEBUG ("Auth failed for account %s",
119 tp_proxy_get_object_path (data->account));
121 tp_channel_close_async (data->channel, NULL, NULL);
122 auth_data_free (data);
125 static void
126 auth_cb (GObject *source,
127 GAsyncResult *result,
128 gpointer user_data)
130 TpChannel *channel = (TpChannel *) source;
131 AuthData *data = user_data;
132 GError *error = NULL;
134 if (!empathy_sasl_auth_finish (channel, result, &error))
136 DEBUG ("SASL Mechanism error: %s", error->message);
137 fail_auth (data);
138 g_clear_error (&error);
139 return;
142 /* Success! */
143 tp_channel_close_async (channel, NULL, NULL);
144 auth_data_free (data);
147 static void
148 got_oauth2_access_token_cb (GObject *source,
149 GAsyncResult *result,
150 gpointer user_data)
152 GoaOAuth2Based *oauth2 = (GoaOAuth2Based *) source;
153 AuthData *data = user_data;
154 gchar *access_token;
155 gint expires_in;
156 GError *error = NULL;
158 if (!goa_oauth2_based_call_get_access_token_finish (oauth2,
159 &access_token, &expires_in, result, &error))
161 DEBUG ("Failed to get access token: %s", error->message);
162 fail_auth (data);
163 g_clear_error (&error);
164 return;
167 DEBUG ("Got access token for %s:\n%s",
168 tp_proxy_get_object_path (data->account),
169 access_token);
171 switch (empathy_sasl_channel_select_mechanism (data->channel))
173 case EMPATHY_SASL_MECHANISM_FACEBOOK:
174 empathy_sasl_auth_facebook_async (data->channel,
175 goa_oauth2_based_get_client_id (oauth2), access_token,
176 auth_cb, data);
177 break;
179 case EMPATHY_SASL_MECHANISM_WLM:
180 empathy_sasl_auth_wlm_async (data->channel,
181 access_token,
182 auth_cb, data);
183 break;
185 case EMPATHY_SASL_MECHANISM_GOOGLE:
186 empathy_sasl_auth_google_async (data->channel,
187 goa_account_get_identity (goa_object_peek_account (data->goa_object)),
188 access_token, auth_cb, data);
189 break;
191 default:
192 g_assert_not_reached ();
195 g_free (access_token);
198 static void
199 got_password_passwd_cb (GObject *source,
200 GAsyncResult *result,
201 gpointer user_data)
203 GoaPasswordBased *password = (GoaPasswordBased *) source;
204 AuthData *data = user_data;
205 gchar *passwd;
206 GError *error = NULL;
208 if (!goa_password_based_call_get_password_finish (password,
209 &passwd, result, &error))
211 DEBUG ("Failed to get password: %s", error->message);
212 fail_auth (data);
213 g_clear_error (&error);
214 return;
217 DEBUG ("Got password for %s", tp_proxy_get_object_path (data->account));
219 empathy_sasl_auth_password_async (data->channel, passwd, auth_cb, data);
220 g_free (passwd);
223 static void
224 ensure_credentials_cb (GObject *source,
225 GAsyncResult *result,
226 gpointer user_data)
228 AuthData *data = user_data;
229 GoaAccount *goa_account = (GoaAccount *) source;
230 GoaOAuth2Based *oauth2;
231 GoaPasswordBased *password;
232 EmpathySaslMechanism mech;
233 gboolean supports_password;
234 gint expires_in;
235 GError *error = NULL;
237 if (!goa_account_call_ensure_credentials_finish (goa_account, &expires_in,
238 result, &error))
240 DEBUG ("Failed to EnsureCredentials: %s", error->message);
241 fail_auth (data);
242 g_clear_error (&error);
243 return;
246 /* We prefer oauth2, if available */
247 oauth2 = goa_object_get_oauth2_based (data->goa_object);
248 mech = empathy_sasl_channel_select_mechanism (data->channel);
249 if (oauth2 != NULL && mech != EMPATHY_SASL_MECHANISM_PASSWORD)
251 DEBUG ("Goa daemon has credentials for %s, get the access token",
252 tp_proxy_get_object_path (data->account));
254 goa_oauth2_based_call_get_access_token (oauth2, NULL,
255 got_oauth2_access_token_cb, data);
257 g_object_unref (oauth2);
258 return;
261 /* Else we use the password */
262 password = goa_object_get_password_based (data->goa_object);
263 supports_password = empathy_sasl_channel_supports_mechanism (data->channel,
264 "X-TELEPATHY-PASSWORD");
265 if (password != NULL && supports_password)
267 DEBUG ("Goa daemon has credentials for %s, get the password",
268 tp_proxy_get_object_path (data->account));
270 /* arg_id is currently unused */
271 goa_password_based_call_get_password (password, "", NULL,
272 got_password_passwd_cb, data);
274 g_object_unref (password);
275 return;
278 DEBUG ("GoaObject does not implement oauth2 or password");
279 fail_auth (data);
282 static void
283 start_auth (AuthData *data)
285 EmpathyGoaAuthHandler *self = data->self;
286 const GValue *id_value;
287 const gchar *id;
288 GList *goa_accounts, *l;
289 gboolean found = FALSE;
291 id_value = tp_account_get_storage_identifier (data->account);
292 id = g_value_get_string (id_value);
294 goa_accounts = goa_client_get_accounts (self->priv->client);
295 for (l = goa_accounts; l != NULL && !found; l = l->next)
297 GoaObject *goa_object = l->data;
298 GoaAccount *goa_account;
300 goa_account = goa_object_get_account (goa_object);
301 if (!tp_strdiff (goa_account_get_id (goa_account), id))
303 data->goa_object = g_object_ref (goa_object);
305 DEBUG ("Found the GoaAccount for %s, ensure credentials",
306 tp_proxy_get_object_path (data->account));
308 goa_account_call_ensure_credentials (goa_account, NULL,
309 ensure_credentials_cb, data);
311 found = TRUE;
314 g_object_unref (goa_account);
316 g_list_free_full (goa_accounts, g_object_unref);
318 if (!found)
320 DEBUG ("Cannot find GoaAccount");
321 fail_auth (data);
325 static void
326 client_new_cb (GObject *source,
327 GAsyncResult *result,
328 gpointer user_data)
330 EmpathyGoaAuthHandler *self = user_data;
331 GList *l;
332 GError *error = NULL;
334 self->priv->client_preparing = FALSE;
335 self->priv->client = goa_client_new_finish (result, &error);
336 if (self->priv->client == NULL)
338 DEBUG ("Error getting GoaClient: %s", error->message);
339 g_clear_error (&error);
342 /* process queued data */
343 for (l = self->priv->auth_queue; l != NULL; l = l->next)
345 AuthData *data = l->data;
347 if (self->priv->client != NULL)
348 start_auth (data);
349 else
350 fail_auth (data);
353 tp_clear_pointer (&self->priv->auth_queue, g_list_free);
356 void
357 empathy_goa_auth_handler_start (EmpathyGoaAuthHandler *self,
358 TpChannel *channel,
359 TpAccount *account)
361 AuthData *data;
363 g_return_if_fail (TP_IS_CHANNEL (channel));
364 g_return_if_fail (TP_IS_ACCOUNT (account));
365 g_return_if_fail (empathy_goa_auth_handler_supports (self, channel, account));
367 DEBUG ("Start Goa auth for account: %s",
368 tp_proxy_get_object_path (account));
370 data = auth_data_new (self, channel, account);
372 if (self->priv->client == NULL)
374 /* GOA client not ready yet, queue data */
375 if (!self->priv->client_preparing)
377 goa_client_new (NULL, client_new_cb, self);
378 self->priv->client_preparing = TRUE;
381 self->priv->auth_queue = g_list_prepend (self->priv->auth_queue, data);
383 else
385 start_auth (data);
389 gboolean
390 empathy_goa_auth_handler_supports (EmpathyGoaAuthHandler *self,
391 TpChannel *channel,
392 TpAccount *account)
394 const gchar *provider;
395 EmpathySaslMechanism mech;
397 g_return_val_if_fail (TP_IS_CHANNEL (channel), FALSE);
398 g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
400 provider = tp_account_get_storage_provider (account);
401 if (tp_strdiff (provider, EMPATHY_GOA_PROVIDER))
402 return FALSE;
404 mech = empathy_sasl_channel_select_mechanism (channel);
405 return mech == EMPATHY_SASL_MECHANISM_FACEBOOK ||
406 mech == EMPATHY_SASL_MECHANISM_WLM ||
407 mech == EMPATHY_SASL_MECHANISM_GOOGLE ||
408 mech == EMPATHY_SASL_MECHANISM_PASSWORD;