]> git.0d.be Git - empathy.git/blob - libempathy/empathy-server-sasl-handler.c
Merge remote branch 'glassrose/accounts-supporting-chatrooms-only-603027'
[empathy.git] / libempathy / empathy-server-sasl-handler.c
1 /*
2  * empathy-server-sasl-handler.c - Source for EmpathyServerSASLHandler
3  * Copyright (C) 2010 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "empathy-server-sasl-handler.h"
21
22 #include <telepathy-glib/util.h>
23
24 #include <string.h>
25
26 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
27 #include "empathy-debug.h"
28 #include "empathy-keyring.h"
29
30 enum {
31   PROP_CHANNEL = 1,
32   PROP_ACCOUNT,
33   LAST_PROPERTY,
34 };
35
36 /* signal enum */
37 enum {
38   INVALIDATED,
39   LAST_SIGNAL,
40 };
41
42 static guint signals[LAST_SIGNAL] = {0};
43
44 typedef struct {
45   TpChannel *channel;
46   TpAccount *account;
47
48   GSimpleAsyncResult *result;
49
50   gchar *password;
51
52   GSimpleAsyncResult *async_init_res;
53 } EmpathyServerSASLHandlerPriv;
54
55 static void async_initable_iface_init (GAsyncInitableIface *iface);
56
57 G_DEFINE_TYPE_WITH_CODE (EmpathyServerSASLHandler, empathy_server_sasl_handler,
58     G_TYPE_OBJECT,
59     G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init));
60
61 static const gchar *sasl_statuses[] = {
62   "not started",
63   "in progress",
64   "server succeeded",
65   "client accepted",
66   "succeeded",
67   "server failed",
68   "client failed",
69 };
70
71 static void
72 sasl_status_changed_cb (TpChannel *channel,
73     TpSASLStatus status,
74     const gchar *error,
75     GHashTable *details,
76     gpointer user_data,
77     GObject *weak_object)
78 {
79   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (weak_object)->priv;
80
81   /* buh boh */
82   if (status >= G_N_ELEMENTS (sasl_statuses))
83     {
84       DEBUG ("SASL status changed to unknown status");
85       return;
86     }
87
88   DEBUG ("SASL status changed to '%s'", sasl_statuses[status]);
89
90   if (status == TP_SASL_STATUS_SERVER_SUCCEEDED)
91     {
92       DEBUG ("Calling AcceptSASL");
93       tp_cli_channel_interface_sasl_authentication_call_accept_sasl (
94           priv->channel, -1, NULL, NULL, NULL, NULL);
95     }
96   else if (status == TP_SASL_STATUS_SUCCEEDED)
97     {
98       DEBUG ("SASL succeeded, calling Close");
99       tp_cli_channel_call_close (priv->channel, -1,
100           NULL, NULL, NULL, NULL);
101     }
102 }
103
104 static gboolean
105 empathy_server_sasl_handler_give_password (gpointer data)
106 {
107   EmpathyServerSASLHandler *self = data;
108   EmpathyServerSASLHandlerPriv *priv = self->priv;
109
110   empathy_server_sasl_handler_provide_password (self,
111       priv->password, FALSE);
112
113   return FALSE;
114 }
115
116 static void
117 empathy_server_sasl_handler_get_password_async_cb (GObject *source,
118     GAsyncResult *result,
119     gpointer user_data)
120 {
121   EmpathyServerSASLHandlerPriv *priv;
122   const gchar *password;
123   GError *error = NULL;
124
125   priv = EMPATHY_SERVER_SASL_HANDLER (user_data)->priv;
126
127   password = empathy_keyring_get_password_finish (TP_ACCOUNT (source),
128       result, &error);
129
130   if (password != NULL)
131     {
132       priv->password = g_strdup (password);
133
134       /* Do this in an idle so the async result will get there
135        * first. */
136       g_idle_add (empathy_server_sasl_handler_give_password, user_data);
137     }
138
139   g_simple_async_result_complete (priv->async_init_res);
140   tp_clear_object (&priv->async_init_res);
141 }
142
143 static void
144 empathy_server_sasl_handler_init_async (GAsyncInitable *initable,
145     gint io_priority,
146     GCancellable *cancellable,
147     GAsyncReadyCallback callback,
148     gpointer user_data)
149 {
150   EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (initable);
151   EmpathyServerSASLHandlerPriv *priv = self->priv;
152
153   g_assert (priv->account != NULL);
154
155   priv->async_init_res = g_simple_async_result_new (G_OBJECT (self),
156       callback, user_data, empathy_server_sasl_handler_new_async);
157
158   empathy_keyring_get_password_async (priv->account,
159       empathy_server_sasl_handler_get_password_async_cb, self);
160 }
161
162 static gboolean
163 empathy_server_sasl_handler_init_finish (GAsyncInitable *initable,
164     GAsyncResult *res,
165     GError **error)
166 {
167   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
168           error))
169     return FALSE;
170
171   return TRUE;
172 }
173
174 static void
175 async_initable_iface_init (GAsyncInitableIface *iface)
176 {
177   iface->init_async = empathy_server_sasl_handler_init_async;
178   iface->init_finish = empathy_server_sasl_handler_init_finish;
179 }
180
181 static void
182 channel_invalidated_cb (TpProxy *proxy,
183     guint domain,
184     gint code,
185     gchar *message,
186     EmpathyServerSASLHandler *self)
187 {
188   g_signal_emit (self, signals[INVALIDATED], 0);
189 }
190
191 static void
192 empathy_server_sasl_handler_constructed (GObject *object)
193 {
194   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
195   GError *error = NULL;
196
197   tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (
198       priv->channel, sasl_status_changed_cb, NULL, NULL, object, &error);
199
200   if (error != NULL)
201     {
202       DEBUG ("Failed to connect to SASLStatusChanged: %s", error->message);
203       g_clear_error (&error);
204     }
205
206   tp_g_signal_connect_object (priv->channel, "invalidated",
207       G_CALLBACK (channel_invalidated_cb), object, 0);
208 }
209
210 static void
211 empathy_server_sasl_handler_get_property (GObject *object,
212     guint property_id,
213     GValue *value,
214     GParamSpec *pspec)
215 {
216   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
217
218   switch (property_id)
219     {
220     case PROP_CHANNEL:
221       g_value_set_object (value, priv->channel);
222       break;
223     case PROP_ACCOUNT:
224       g_value_set_object (value, priv->account);
225       break;
226     default:
227       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
228       break;
229     }
230 }
231
232 static void
233 empathy_server_sasl_handler_set_property (GObject *object,
234     guint property_id,
235     const GValue *value,
236     GParamSpec *pspec)
237 {
238   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
239
240   switch (property_id)
241     {
242     case PROP_CHANNEL:
243       priv->channel = g_value_dup_object (value);
244       break;
245     case PROP_ACCOUNT:
246       priv->account = g_value_dup_object (value);
247       break;
248     default:
249       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
250       break;
251     }
252 }
253
254 static void
255 empathy_server_sasl_handler_dispose (GObject *object)
256 {
257   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
258
259   DEBUG ("%p", object);
260
261   tp_clear_object (&priv->channel);
262   tp_clear_object (&priv->account);
263
264   G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->dispose (object);
265 }
266
267 static void
268 empathy_server_sasl_handler_finalize (GObject *object)
269 {
270   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
271
272   DEBUG ("%p", object);
273
274   tp_clear_pointer (&priv->password, g_free);
275
276   G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->finalize (object);
277 }
278
279 static void
280 empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass *klass)
281 {
282   GObjectClass *oclass = G_OBJECT_CLASS (klass);
283   GParamSpec *pspec;
284
285   oclass->constructed = empathy_server_sasl_handler_constructed;
286   oclass->get_property = empathy_server_sasl_handler_get_property;
287   oclass->set_property = empathy_server_sasl_handler_set_property;
288   oclass->dispose = empathy_server_sasl_handler_dispose;
289   oclass->dispose = empathy_server_sasl_handler_finalize;
290
291   g_type_class_add_private (klass, sizeof (EmpathyServerSASLHandlerPriv));
292
293   pspec = g_param_spec_object ("channel", "The TpChannel",
294       "The TpChannel this handler is supposed to handle.",
295       TP_TYPE_CHANNEL,
296       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
297   g_object_class_install_property (oclass, PROP_CHANNEL, pspec);
298
299   pspec = g_param_spec_object ("account", "The TpAccount",
300       "The TpAccount this channel belongs to.",
301       TP_TYPE_ACCOUNT,
302       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
303   g_object_class_install_property (oclass, PROP_ACCOUNT, pspec);
304
305   signals[INVALIDATED] = g_signal_new ("invalidated",
306       G_TYPE_FROM_CLASS (klass),
307       G_SIGNAL_RUN_LAST, 0,
308       NULL, NULL,
309       g_cclosure_marshal_VOID__VOID,
310       G_TYPE_NONE, 0);
311 }
312
313 static void
314 empathy_server_sasl_handler_init (EmpathyServerSASLHandler *self)
315 {
316   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
317       EMPATHY_TYPE_SERVER_SASL_HANDLER, EmpathyServerSASLHandlerPriv);
318 }
319
320 EmpathyServerSASLHandler *
321 empathy_server_sasl_handler_new_finish (GAsyncResult *result,
322     GError **error)
323 {
324   GObject *object, *source_object;
325
326   source_object = g_async_result_get_source_object (result);
327
328   object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
329       result, error);
330   g_object_unref (source_object);
331
332   if (object != NULL)
333     return EMPATHY_SERVER_SASL_HANDLER (object);
334   else
335     return NULL;
336 }
337
338 void
339 empathy_server_sasl_handler_new_async (TpAccount *account,
340     TpChannel *channel,
341     GAsyncReadyCallback callback,
342     gpointer user_data)
343 {
344   g_return_if_fail (TP_IS_ACCOUNT (account));
345   g_return_if_fail (TP_IS_CHANNEL (channel));
346   g_return_if_fail (callback != NULL);
347
348   g_async_initable_new_async (EMPATHY_TYPE_SERVER_SASL_HANDLER,
349       G_PRIORITY_DEFAULT, NULL, callback, user_data,
350       "account", account,
351       "channel", channel,
352       NULL);
353 }
354
355 static void
356 start_mechanism_with_data_cb (TpChannel *proxy,
357     const GError *error,
358     gpointer user_data,
359     GObject *weak_object)
360 {
361   if (error != NULL)
362     {
363       DEBUG ("Failed to start mechanism: %s", error->message);
364       return;
365     }
366
367   DEBUG ("Started mechanism successfully");
368 }
369
370 static void
371 empathy_server_sasl_handler_set_password_cb (GObject *source,
372     GAsyncResult *result,
373     gpointer user_data)
374 {
375   GError *error = NULL;
376
377   if (!empathy_keyring_set_password_finish (TP_ACCOUNT (source), result,
378           &error))
379     {
380       DEBUG ("Failed to set password: %s", error->message);
381       g_clear_error (&error);
382     }
383   else
384     {
385       DEBUG ("Password set successfully.");
386     }
387 }
388
389 void
390 empathy_server_sasl_handler_provide_password (
391     EmpathyServerSASLHandler *handler,
392     const gchar *password,
393     gboolean remember)
394 {
395   EmpathyServerSASLHandlerPriv *priv;
396   GArray *array;
397
398   g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
399
400   priv = handler->priv;
401
402   array = g_array_sized_new (TRUE, FALSE,
403       sizeof (gchar), strlen (password));
404
405   g_array_append_vals (array, password, strlen (password));
406
407   DEBUG ("Calling StartMechanismWithData with our password");
408
409   tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
410       priv->channel, -1, "X-TELEPATHY-PASSWORD", array,
411       start_mechanism_with_data_cb, NULL, NULL, G_OBJECT (handler));
412
413   g_array_unref (array);
414
415   DEBUG ("%sremembering the password", remember ? "" : "not ");
416
417   if (remember)
418     {
419       empathy_keyring_set_password_async (priv->account, password,
420           empathy_server_sasl_handler_set_password_cb, NULL);
421     }
422 }
423
424 void
425 empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler)
426 {
427   EmpathyServerSASLHandlerPriv *priv;
428
429   g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
430
431   priv = handler->priv;
432
433   DEBUG ("Cancelling SASL mechanism...");
434
435   tp_cli_channel_interface_sasl_authentication_call_abort_sasl (
436       priv->channel, -1, TP_SASL_ABORT_REASON_USER_ABORT,
437       "User cancelled the authentication",
438       NULL, NULL, NULL, NULL);
439 }
440
441 TpAccount *
442 empathy_server_sasl_handler_get_account (EmpathyServerSASLHandler *handler)
443 {
444   EmpathyServerSASLHandlerPriv *priv;
445
446   g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
447
448   priv = handler->priv;
449
450   return priv->account;
451 }
452
453 gboolean
454 empathy_server_sasl_handler_has_password (EmpathyServerSASLHandler *handler)
455 {
456   EmpathyServerSASLHandlerPriv *priv;
457
458   g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), FALSE);
459
460   priv = handler->priv;
461
462   return (priv->password != NULL);
463 }