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>
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
22 #include "empathy-sasl-mechanisms.h"
24 #include <libsoup/soup.h>
25 #include <tp-account-widgets/tpaw-utils.h>
26 #include <telepathy-glib/telepathy-glib-dbus.h>
28 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
29 #include "empathy-debug.h"
30 #include "empathy-utils.h"
32 #define MECH_FACEBOOK "X-FACEBOOK-PLATFORM"
33 #define MECH_WLM "X-MESSENGER-OAUTH2"
34 #define MECH_GOOGLE "X-OAUTH2"
35 #define MECH_PASSWORD "X-TELEPATHY-PASSWORD"
39 EmpathySaslMechanism id
;
43 static SupportedMech supported_mechanisms
[] = {
44 { EMPATHY_SASL_MECHANISM_FACEBOOK
, MECH_FACEBOOK
},
45 { EMPATHY_SASL_MECHANISM_WLM
, MECH_WLM
},
46 { EMPATHY_SASL_MECHANISM_GOOGLE
, MECH_GOOGLE
},
48 /* Must be the last one, otherwise empathy_sasl_channel_select_mechanism()
49 * will prefer password over web auth for servers supporting both. */
50 { EMPATHY_SASL_MECHANISM_PASSWORD
, MECH_PASSWORD
}
54 generic_cb (TpChannel
*proxy
,
59 GSimpleAsyncResult
*result
= user_data
;
63 g_simple_async_result_set_from_error (result
, error
);
64 g_simple_async_result_complete (result
);
69 sasl_status_changed_cb (TpChannel
*channel
,
71 const gchar
*dbus_error
,
76 GSimpleAsyncResult
*result
= user_data
;
80 case TP_SASL_STATUS_SERVER_SUCCEEDED
:
81 tp_cli_channel_interface_sasl_authentication_call_accept_sasl (channel
,
82 -1, generic_cb
, g_object_ref (result
), g_object_unref
, NULL
);
85 case TP_SASL_STATUS_SERVER_FAILED
:
86 case TP_SASL_STATUS_CLIENT_FAILED
:
90 tp_proxy_dbus_error_to_gerror (channel
, dbus_error
,
91 tp_asv_get_string (details
, "debug-message"), &error
);
93 DEBUG ("SASL failed: %s", error
->message
);
95 g_simple_async_result_take_error (result
, error
);
96 g_simple_async_result_complete (result
);
100 case TP_SASL_STATUS_SUCCEEDED
:
101 DEBUG ("SASL succeeded");
103 g_simple_async_result_complete (result
);
111 static GSimpleAsyncResult
*
112 empathy_sasl_auth_common_async (TpChannel
*channel
,
113 GAsyncReadyCallback callback
,
116 GSimpleAsyncResult
*result
;
117 GError
*error
= NULL
;
119 g_return_val_if_fail (TP_IS_CHANNEL (channel
), NULL
);
120 g_return_val_if_fail (tp_proxy_has_interface_by_id (channel
,
121 TP_IFACE_QUARK_CHANNEL_INTERFACE_SASL_AUTHENTICATION
), NULL
);
123 result
= g_simple_async_result_new ((GObject
*) channel
,
124 callback
, user_data
, empathy_sasl_auth_common_async
);
126 tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (
127 channel
, sasl_status_changed_cb
,
128 g_object_ref (result
), g_object_unref
, NULL
, &error
);
129 g_assert_no_error (error
);
142 facebook_data_free (FacebookData
*data
)
144 g_object_unref (data
->channel
);
145 g_free (data
->client_id
);
146 g_free (data
->access_token
);
147 g_slice_free (FacebookData
, data
);
151 facebook_new_challenge_cb (TpChannel
*channel
,
152 const GArray
*challenge
,
154 GObject
*weak_object
)
156 GSimpleAsyncResult
*result
= user_data
;
159 GString
*response_string
;
160 GArray
*response_array
;
162 DEBUG ("new challenge: %s", challenge
->data
);
164 data
= g_simple_async_result_get_op_res_gpointer (result
);
166 h
= soup_form_decode (challenge
->data
);
168 /* See https://developers.facebook.com/docs/chat/#platauth.
169 * We don't use soup_form_encode() here because it would escape parameters
170 * and facebook server is not expecting that and would reject the response. */
171 response_string
= g_string_new ("v=1.0&call_id=0");
172 g_string_append (response_string
, "&access_token=");
173 g_string_append_uri_escaped (response_string
, data
->access_token
, NULL
, TRUE
);
174 g_string_append (response_string
, "&api_key=");
175 g_string_append_uri_escaped (response_string
, data
->client_id
, NULL
, TRUE
);
176 g_string_append (response_string
, "&method=");
177 g_string_append_uri_escaped (response_string
, g_hash_table_lookup (h
, "method"), NULL
, TRUE
);
178 g_string_append (response_string
, "&nonce=");
179 g_string_append_uri_escaped (response_string
, g_hash_table_lookup (h
, "nonce"), NULL
, TRUE
);
181 DEBUG ("Response: %s", response_string
->str
);
183 response_array
= g_array_new (FALSE
, FALSE
, sizeof (gchar
));
184 g_array_append_vals (response_array
, response_string
->str
, response_string
->len
);
186 tp_cli_channel_interface_sasl_authentication_call_respond (data
->channel
, -1,
187 response_array
, generic_cb
, g_object_ref (result
), g_object_unref
, NULL
);
189 g_hash_table_unref (h
);
190 g_string_free (response_string
, TRUE
);
191 g_array_unref (response_array
);
195 empathy_sasl_auth_facebook_async (TpChannel
*channel
,
196 const gchar
*client_id
,
197 const gchar
*access_token
,
198 GAsyncReadyCallback callback
,
201 GSimpleAsyncResult
*result
;
203 GError
*error
= NULL
;
205 result
= empathy_sasl_auth_common_async (channel
, callback
, user_data
);
207 g_return_if_fail (result
!= NULL
);
208 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel
,
210 g_return_if_fail (!tp_str_empty (client_id
));
211 g_return_if_fail (!tp_str_empty (access_token
));
213 DEBUG ("Start %s mechanism", MECH_FACEBOOK
);
215 data
= g_slice_new0 (FacebookData
);
216 data
->channel
= g_object_ref (channel
);
217 data
->client_id
= g_strdup (client_id
);
218 data
->access_token
= g_strdup (access_token
);
220 g_simple_async_result_set_op_res_gpointer (result
, data
,
221 (GDestroyNotify
) facebook_data_free
);
223 tp_cli_channel_interface_sasl_authentication_connect_to_new_challenge (
224 channel
, facebook_new_challenge_cb
,
225 g_object_ref (result
), g_object_unref
,
227 g_assert_no_error (error
);
229 tp_cli_channel_interface_sasl_authentication_call_start_mechanism (
230 channel
, -1, MECH_FACEBOOK
, generic_cb
,
231 g_object_ref (result
), g_object_unref
, NULL
);
233 g_object_unref (result
);
237 empathy_sasl_auth_wlm_async (TpChannel
*channel
,
238 const gchar
*access_token
,
239 GAsyncReadyCallback callback
,
242 GSimpleAsyncResult
*result
;
243 guchar
*token_decoded
;
244 gsize token_decoded_len
;
245 GArray
*token_decoded_array
;
247 result
= empathy_sasl_auth_common_async (channel
, callback
, user_data
);
249 g_return_if_fail (result
!= NULL
);
250 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel
,
252 g_return_if_fail (!tp_str_empty (access_token
));
254 DEBUG ("Start %s mechanism", MECH_WLM
);
256 /* Wocky will base64 encode, but token actually already is base64, so we
257 * decode now and it will be re-encoded. */
258 token_decoded
= g_base64_decode (access_token
, &token_decoded_len
);
259 token_decoded_array
= g_array_new (FALSE
, FALSE
, sizeof (guchar
));
260 g_array_append_vals (token_decoded_array
, token_decoded
, token_decoded_len
);
262 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
263 channel
, -1, MECH_WLM
, token_decoded_array
,
264 generic_cb
, g_object_ref (result
), g_object_unref
, NULL
);
266 g_array_unref (token_decoded_array
);
267 g_free (token_decoded
);
268 g_object_unref (result
);
272 empathy_sasl_auth_google_async (TpChannel
*channel
,
273 const gchar
*username
,
274 const gchar
*access_token
,
275 GAsyncReadyCallback callback
,
278 GSimpleAsyncResult
*result
;
281 result
= empathy_sasl_auth_common_async (channel
, callback
, user_data
);
283 g_return_if_fail (result
!= NULL
);
284 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel
,
286 g_return_if_fail (!tp_str_empty (username
));
287 g_return_if_fail (!tp_str_empty (access_token
));
289 DEBUG ("Start %s mechanism", MECH_GOOGLE
);
291 credential
= g_array_sized_new (FALSE
, FALSE
, sizeof (gchar
),
292 strlen (access_token
) + strlen (username
) + 2);
294 g_array_append_val (credential
, "\0");
295 g_array_append_vals (credential
, username
, strlen (username
));
296 g_array_append_val (credential
, "\0");
297 g_array_append_vals (credential
, access_token
, strlen (access_token
));
299 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
300 channel
, -1, MECH_GOOGLE
, credential
,
301 generic_cb
, g_object_ref (result
), g_object_unref
, NULL
);
303 g_array_unref (credential
);
304 g_object_unref (result
);
308 empathy_sasl_auth_password_async (TpChannel
*channel
,
309 const gchar
*password
,
310 GAsyncReadyCallback callback
,
313 GSimpleAsyncResult
*result
;
314 GArray
*password_array
;
316 result
= empathy_sasl_auth_common_async (channel
, callback
, user_data
);
318 g_return_if_fail (result
!= NULL
);
319 g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel
,
321 g_return_if_fail (!tp_str_empty (password
));
323 DEBUG ("Start %s mechanism", MECH_PASSWORD
);
325 password_array
= g_array_sized_new (FALSE
, FALSE
, sizeof (gchar
),
327 g_array_append_vals (password_array
, password
, strlen (password
));
329 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
330 channel
, -1, MECH_PASSWORD
, password_array
,
331 generic_cb
, g_object_ref (result
), g_object_unref
, NULL
);
333 g_array_unref (password_array
);
334 g_object_unref (result
);
338 empathy_sasl_auth_finish (TpChannel
*channel
,
339 GAsyncResult
*result
,
342 tpaw_implement_finish_void (channel
, empathy_sasl_auth_common_async
);
346 empathy_sasl_channel_supports_mechanism (TpChannel
*channel
,
347 const gchar
*mechanism
)
350 GStrv available_mechanisms
;
353 props
= tp_channel_dup_immutable_properties (channel
);
355 g_variant_lookup (props
,
356 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS
,
357 "^as", &available_mechanisms
);
359 result
= tp_strv_contains ((const gchar
* const *) available_mechanisms
,
362 g_variant_unref (props
);
363 g_strfreev (available_mechanisms
);
368 empathy_sasl_channel_select_mechanism (TpChannel
*channel
)
372 for (i
= 0; i
< G_N_ELEMENTS (supported_mechanisms
); i
++)
374 if (empathy_sasl_channel_supports_mechanism (channel
,
375 supported_mechanisms
[i
].name
))
376 return supported_mechanisms
[i
].id
;
379 return EMPATHY_SASL_MECHANISM_UNSUPPORTED
;