]> git.0d.be Git - empathy.git/blob - libempathy/empathy-sasl-mechanisms.c
Updated kn translation
[empathy.git] / libempathy / empathy-sasl-mechanisms.c
1 /*
2  * empathy-sasl-mechanisms.h - Header for SASL authentication mechanisms
3  * Copyright (C) 2012 Collabora Ltd.
4  * @author Xavier Claessens <xavier.claessens@collabora.co.uk>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include <libsoup/soup.h>
24 #include <string.h>
25
26 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
27 #include "empathy-debug.h"
28 #include "empathy-utils.h"
29 #include "empathy-sasl-mechanisms.h"
30
31 #define MECH_FACEBOOK "X-FACEBOOK-PLATFORM"
32 #define MECH_WLM "X-MESSENGER-OAUTH2"
33 #define MECH_GOOGLE "X-OAUTH2"
34
35 typedef struct
36 {
37   EmpathySaslMechanism id;
38   const gchar *name;
39 } SupportedMech;
40
41 static SupportedMech supported_mechanisms[] = {
42   { EMPATHY_SASL_MECHANISM_FACEBOOK, MECH_FACEBOOK },
43   { EMPATHY_SASL_MECHANISM_WLM, MECH_WLM },
44   { EMPATHY_SASL_MECHANISM_GOOGLE, MECH_GOOGLE },
45 };
46
47 static void
48 generic_cb (TpChannel *proxy,
49     const GError *error,
50     gpointer user_data,
51     GObject *weak_object)
52 {
53   GSimpleAsyncResult *result = user_data;
54
55   if (error != NULL)
56     {
57       g_simple_async_result_set_from_error (result, error);
58       g_simple_async_result_complete (result);
59     }
60 }
61
62 static void
63 sasl_status_changed_cb (TpChannel *channel,
64     guint status,
65     const gchar *dbus_error,
66     GHashTable *details,
67     gpointer user_data,
68     GObject *self)
69 {
70   GSimpleAsyncResult *result = user_data;
71
72   switch (status)
73     {
74       case TP_SASL_STATUS_SERVER_SUCCEEDED:
75         tp_cli_channel_interface_sasl_authentication_call_accept_sasl (channel,
76             -1, generic_cb, g_object_ref (result), g_object_unref, NULL);
77         break;
78
79       case TP_SASL_STATUS_SERVER_FAILED:
80       case TP_SASL_STATUS_CLIENT_FAILED:
81         {
82           GError *error = NULL;
83
84           tp_proxy_dbus_error_to_gerror (channel, dbus_error,
85               tp_asv_get_string (details, "debug-message"), &error);
86
87           DEBUG ("SASL failed: %s", error->message);
88
89           g_simple_async_result_take_error (result, error);
90         }
91         break;
92
93       case TP_SASL_STATUS_SUCCEEDED:
94         DEBUG ("SASL succeeded");
95
96         g_simple_async_result_complete (result);
97         break;
98
99       default:
100         break;
101     }
102 }
103
104 static GSimpleAsyncResult *
105 empathy_sasl_auth_common_async (TpChannel *channel,
106     GAsyncReadyCallback callback,
107     gpointer user_data)
108 {
109   GSimpleAsyncResult *result;
110   GError *error = NULL;
111
112   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
113   g_return_val_if_fail (tp_proxy_has_interface_by_id (channel,
114       TP_IFACE_QUARK_CHANNEL_INTERFACE_SASL_AUTHENTICATION), NULL);
115
116   result = g_simple_async_result_new ((GObject *) channel,
117       callback, user_data, empathy_sasl_auth_common_async);
118
119   tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (
120       channel, sasl_status_changed_cb,
121       g_object_ref (result), g_object_unref, NULL, &error);
122   g_assert_no_error (error);
123
124   return result;
125 }
126
127 typedef struct
128 {
129   TpChannel *channel;
130   gchar *client_id;
131   gchar *access_token;
132 } FacebookData;
133
134 static void
135 facebook_data_free (FacebookData *data)
136 {
137   g_object_unref (data->channel);
138   g_free (data->client_id);
139   g_free (data->access_token);
140   g_slice_free (FacebookData, data);
141 }
142
143 static void
144 facebook_new_challenge_cb (TpChannel *channel,
145     const GArray *challenge,
146     gpointer user_data,
147     GObject *weak_object)
148 {
149   GSimpleAsyncResult *result = user_data;
150   FacebookData *data;
151   GHashTable *h;
152   GHashTable *params;
153   gchar *response;
154   GArray *response_array;
155
156   DEBUG ("new challenge: %s", challenge->data);
157
158   data = g_simple_async_result_get_op_res_gpointer (result);
159
160   h = soup_form_decode (challenge->data);
161
162   /* See https://developers.facebook.com/docs/chat/#platauth */
163   params = g_hash_table_new (g_str_hash, g_str_equal);
164   g_hash_table_insert (params, "method", g_hash_table_lookup (h, "method"));
165   g_hash_table_insert (params, "nonce", g_hash_table_lookup (h, "nonce"));
166   g_hash_table_insert (params, "access_token", data->access_token);
167   g_hash_table_insert (params, "api_key", data->client_id);
168   g_hash_table_insert (params, "call_id", "0");
169   g_hash_table_insert (params, "v", "1.0");
170
171   response = soup_form_encode_hash (params);
172   DEBUG ("Response: %s", response);
173
174   response_array = g_array_new (FALSE, FALSE, sizeof (gchar));
175   g_array_append_vals (response_array, response, strlen (response));
176
177   tp_cli_channel_interface_sasl_authentication_call_respond (data->channel, -1,
178       response_array, generic_cb, g_object_ref (result), g_object_unref, NULL);
179
180   g_hash_table_unref (h);
181   g_hash_table_unref (params);
182   g_free (response);
183   g_array_unref (response_array);
184 }
185
186 void
187 empathy_sasl_auth_facebook_async (TpChannel *channel,
188     const gchar *client_id,
189     const gchar *access_token,
190     GAsyncReadyCallback callback,
191     gpointer user_data)
192 {
193   GSimpleAsyncResult *result;
194   FacebookData *data;
195   GError *error = NULL;
196
197   result = empathy_sasl_auth_common_async (channel, callback, user_data);
198
199   g_return_if_fail (result != NULL);
200   g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel,
201       MECH_FACEBOOK));
202   g_return_if_fail (!tp_str_empty (client_id));
203   g_return_if_fail (!tp_str_empty (access_token));
204
205   DEBUG ("Start %s mechanism", MECH_FACEBOOK);
206
207   data = g_slice_new0 (FacebookData);
208   data->channel = g_object_ref (channel);
209   data->client_id = g_strdup (client_id);
210   data->access_token = g_strdup (access_token);
211
212   g_simple_async_result_set_op_res_gpointer (result, data,
213       (GDestroyNotify) facebook_data_free);
214
215   tp_cli_channel_interface_sasl_authentication_connect_to_new_challenge (
216       channel, facebook_new_challenge_cb,
217       g_object_ref (result), g_object_unref,
218       NULL, &error);
219   g_assert_no_error (error);
220
221   tp_cli_channel_interface_sasl_authentication_call_start_mechanism (
222       channel, -1, MECH_FACEBOOK, generic_cb,
223       g_object_ref (result), g_object_unref, NULL);
224
225   g_object_unref (result);
226 }
227
228 void
229 empathy_sasl_auth_wlm_async (TpChannel *channel,
230     const gchar *access_token,
231     GAsyncReadyCallback callback,
232     gpointer user_data)
233 {
234   GSimpleAsyncResult *result;
235   guchar *token_decoded;
236   gsize token_decoded_len;
237   GArray *token_decoded_array;
238
239   result = empathy_sasl_auth_common_async (channel, callback, user_data);
240
241   g_return_if_fail (result != NULL);
242   g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel,
243       MECH_WLM));
244   g_return_if_fail (!tp_str_empty (access_token));
245
246   DEBUG ("Start %s mechanism", MECH_WLM);
247
248   /* Wocky will base64 encode, but token actually already is base64, so we
249    * decode now and it will be re-encoded. */
250   token_decoded = g_base64_decode (access_token, &token_decoded_len);
251   token_decoded_array = g_array_new (FALSE, FALSE, sizeof (guchar));
252   g_array_append_vals (token_decoded_array, token_decoded, token_decoded_len);
253
254   tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
255       channel, -1, MECH_WLM, token_decoded_array,
256       generic_cb, g_object_ref (result), g_object_unref, NULL);
257
258   g_array_unref (token_decoded_array);
259   g_free (token_decoded);
260   g_object_unref (result);
261 }
262
263 void
264 empathy_sasl_auth_google_async (TpChannel *channel,
265     const gchar *username,
266     const gchar *access_token,
267     GAsyncReadyCallback callback,
268     gpointer user_data)
269 {
270   GSimpleAsyncResult *result;
271   GArray *credential;
272
273   result = empathy_sasl_auth_common_async (channel, callback, user_data);
274
275   g_return_if_fail (result != NULL);
276   g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel,
277       MECH_GOOGLE));
278   g_return_if_fail (!tp_str_empty (username));
279   g_return_if_fail (!tp_str_empty (access_token));
280
281   DEBUG ("Start %s mechanism", MECH_GOOGLE);
282
283   credential = g_array_sized_new (FALSE, FALSE, sizeof (gchar),
284       strlen (access_token) + strlen (username) + 2);
285
286   g_array_append_val (credential, "\0");
287   g_array_append_vals (credential, username, strlen (username));
288   g_array_append_val (credential, "\0");
289   g_array_append_vals (credential, access_token, strlen (access_token));
290
291   tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
292       channel, -1, MECH_GOOGLE, credential,
293       generic_cb, g_object_ref (result), g_object_unref, NULL);
294
295   g_array_unref (credential);
296   g_object_unref (result);
297 }
298
299 gboolean
300 empathy_sasl_auth_finish (TpChannel *channel,
301     GAsyncResult *result,
302     GError **error)
303 {
304   empathy_implement_finish_void (channel, empathy_sasl_auth_common_async);
305 }
306
307 gboolean
308 empathy_sasl_channel_supports_mechanism (TpChannel *channel,
309     const gchar *mechanism)
310 {
311   GHashTable *props;
312   const gchar * const *available_mechanisms;
313
314   props = tp_channel_borrow_immutable_properties (channel);
315   available_mechanisms = tp_asv_get_boxed (props,
316       TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
317       G_TYPE_STRV);
318
319   return tp_strv_contains (available_mechanisms, mechanism);
320 }
321
322 EmpathySaslMechanism
323 empathy_sasl_channel_select_mechanism (TpChannel *channel)
324 {
325   guint i;
326
327   for (i = 0; i < G_N_ELEMENTS (supported_mechanisms); i++)
328     {
329       if (empathy_sasl_channel_supports_mechanism (channel,
330               supported_mechanisms[i].name))
331         return supported_mechanisms[i].id;
332     }
333
334   return EMPATHY_SASL_MECHANISM_UNSUPPORTED;
335 }