]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account-settings.c
Don't destroy MainWindow on delete-event, just hide it
[empathy.git] / libempathy / empathy-account-settings.c
1 /*
2  * empathy-account-settings.c - Source for EmpathyAccountSettings
3  * Copyright (C) 2009 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@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
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/account-manager.h>
26 #include <telepathy-glib/util.h>
27 #include <telepathy-glib/interfaces.h>
28 #include <telepathy-glib/gtypes.h>
29
30 #include "empathy-account-settings.h"
31 #include "empathy-connection-managers.h"
32 #include "empathy-keyring.h"
33 #include "empathy-utils.h"
34 #include "empathy-presence-manager.h"
35
36 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
37 #include <libempathy/empathy-debug.h>
38
39 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountSettings)
40
41 G_DEFINE_TYPE(EmpathyAccountSettings, empathy_account_settings, G_TYPE_OBJECT)
42
43 enum {
44   PROP_ACCOUNT = 1,
45   PROP_CM_NAME,
46   PROP_PROTOCOL,
47   PROP_SERVICE,
48   PROP_DISPLAY_NAME,
49   PROP_DISPLAY_NAME_OVERRIDDEN,
50   PROP_READY
51 };
52
53 enum {
54   PASSWORD_RETRIEVED = 1,
55   LAST_SIGNAL
56 };
57
58 static gulong signals[LAST_SIGNAL] = { 0, };
59
60 /* private structure */
61 typedef struct _EmpathyAccountSettingsPriv EmpathyAccountSettingsPriv;
62
63 struct _EmpathyAccountSettingsPriv
64 {
65   gboolean dispose_has_run;
66   EmpathyConnectionManagers *managers;
67   TpAccountManager *account_manager;
68
69   TpConnectionManager *manager;
70   TpProtocol *protocol_obj;
71
72   TpAccount *account;
73   gchar *cm_name;
74   gchar *protocol;
75   gchar *service;
76   gchar *display_name;
77   gchar *icon_name;
78   gboolean display_name_overridden;
79   gboolean ready;
80
81   gboolean supports_sasl;
82   gboolean password_changed;
83
84   gchar *password;
85   gchar *password_original;
86
87   gboolean password_retrieved;
88   gboolean password_requested;
89
90   /* Parameter name (gchar *) -> parameter value (GValue) */
91   GHashTable *parameters;
92   /* Keys are parameter names from the hash above (gchar *).
93    * Values are regular expresions that should match corresponding parameter
94    * values (GRegex *). Possible regexp patterns are defined in
95    * empathy-account-widget.c */
96   GHashTable *param_regexps;
97   GArray *unset_parameters;
98   GList *required_params;
99
100   gulong managers_ready_id;
101   gboolean preparing_protocol;
102
103   /* If TRUE, the account should have 'tel' in its
104    * Account.Interface.Addressing.URISchemes property. */
105   gboolean uri_scheme_tel;
106
107   GSimpleAsyncResult *apply_result;
108 };
109
110 static void
111 empathy_account_settings_init (EmpathyAccountSettings *obj)
112 {
113   EmpathyAccountSettingsPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE ((obj),
114     EMPATHY_TYPE_ACCOUNT_SETTINGS, EmpathyAccountSettingsPriv);
115
116   obj->priv = priv;
117
118   /* allocate any data required by the object here */
119   priv->managers = empathy_connection_managers_dup_singleton ();
120   priv->account_manager = tp_account_manager_dup ();
121
122   priv->parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
123     g_free, (GDestroyNotify) tp_g_value_slice_free);
124
125   priv->param_regexps = g_hash_table_new_full (g_str_hash, g_str_equal,
126     g_free, (GDestroyNotify) g_regex_unref);
127
128   priv->unset_parameters = g_array_new (TRUE, FALSE, sizeof (gchar *));
129
130   priv->required_params = NULL;
131 }
132
133 static void empathy_account_settings_dispose (GObject *object);
134 static void empathy_account_settings_finalize (GObject *object);
135 static void empathy_account_settings_account_ready_cb (GObject *source_object,
136     GAsyncResult *result, gpointer user_data);
137 static void empathy_account_settings_managers_ready_cb (GObject *obj,
138     GParamSpec *pspec, gpointer user_data);
139 static void empathy_account_settings_check_readyness (
140     EmpathyAccountSettings *self);
141
142 static void
143 empathy_account_settings_set_property (GObject *object,
144     guint prop_id,
145     const GValue *value,
146     GParamSpec *pspec)
147 {
148   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (object);
149   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
150
151   switch (prop_id)
152     {
153       case PROP_ACCOUNT:
154         priv->account = g_value_dup_object (value);
155         break;
156       case PROP_CM_NAME:
157         priv->cm_name = g_value_dup_string (value);
158         break;
159       case PROP_PROTOCOL:
160         priv->protocol = g_value_dup_string (value);
161         break;
162       case PROP_SERVICE:
163         priv->service = g_value_dup_string (value);
164         break;
165       case PROP_DISPLAY_NAME:
166         priv->display_name = g_value_dup_string (value);
167         break;
168       case PROP_DISPLAY_NAME_OVERRIDDEN:
169         priv->display_name_overridden = g_value_get_boolean (value);
170         break;
171       default:
172         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173         break;
174     }
175 }
176
177 static void
178 empathy_account_settings_get_property (GObject *object,
179     guint prop_id,
180     GValue *value,
181     GParamSpec *pspec)
182 {
183   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (object);
184   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
185
186   switch (prop_id)
187     {
188       case PROP_ACCOUNT:
189         g_value_set_object (value, priv->account);
190         break;
191       case PROP_CM_NAME:
192         g_value_set_string (value, priv->cm_name);
193         break;
194       case PROP_PROTOCOL:
195         g_value_set_string (value, priv->protocol);
196         break;
197       case PROP_SERVICE:
198         g_value_set_string (value, priv->service);
199         break;
200       case PROP_DISPLAY_NAME:
201         g_value_set_string (value, priv->display_name);
202         break;
203       case PROP_DISPLAY_NAME_OVERRIDDEN:
204         g_value_set_boolean (value, priv->display_name_overridden);
205         break;
206       case PROP_READY:
207         g_value_set_boolean (value, priv->ready);
208         break;
209       default:
210         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211         break;
212     }
213 }
214
215 static void
216 empathy_account_settings_constructed (GObject *object)
217 {
218   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
219   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
220
221   if (priv->account != NULL)
222     {
223       g_free (priv->cm_name);
224       g_free (priv->protocol);
225       g_free (priv->service);
226
227       priv->cm_name =
228         g_strdup (tp_account_get_connection_manager (priv->account));
229       priv->protocol =
230         g_strdup (tp_account_get_protocol (priv->account));
231       priv->service =
232         g_strdup (tp_account_get_service (priv->account));
233       priv->icon_name = g_strdup
234         (tp_account_get_icon_name (priv->account));
235     }
236   else
237     {
238       priv->icon_name = empathy_protocol_icon_name (priv->protocol);
239     }
240
241   g_assert (priv->cm_name != NULL && priv->protocol != NULL);
242
243   empathy_account_settings_check_readyness (self);
244
245   if (!priv->ready)
246     {
247       GQuark features[] = {
248           TP_ACCOUNT_FEATURE_CORE,
249           TP_ACCOUNT_FEATURE_STORAGE,
250           TP_ACCOUNT_FEATURE_ADDRESSING,
251           0 };
252
253       if (priv->account != NULL)
254         {
255           tp_proxy_prepare_async (priv->account, features,
256               empathy_account_settings_account_ready_cb, self);
257         }
258
259       tp_g_signal_connect_object (priv->managers, "notify::ready",
260         G_CALLBACK (empathy_account_settings_managers_ready_cb), object, 0);
261     }
262
263   if (G_OBJECT_CLASS (
264         empathy_account_settings_parent_class)->constructed != NULL)
265     G_OBJECT_CLASS (
266         empathy_account_settings_parent_class)->constructed (object);
267 }
268
269
270 static void
271 empathy_account_settings_class_init (
272     EmpathyAccountSettingsClass *empathy_account_settings_class)
273 {
274   GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_settings_class);
275
276   g_type_class_add_private (empathy_account_settings_class, sizeof
277       (EmpathyAccountSettingsPriv));
278
279   object_class->dispose = empathy_account_settings_dispose;
280   object_class->finalize = empathy_account_settings_finalize;
281   object_class->set_property = empathy_account_settings_set_property;
282   object_class->get_property = empathy_account_settings_get_property;
283   object_class->constructed = empathy_account_settings_constructed;
284
285   g_object_class_install_property (object_class, PROP_ACCOUNT,
286     g_param_spec_object ("account",
287       "Account",
288       "The TpAccount backing these settings",
289       TP_TYPE_ACCOUNT,
290       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
291
292   g_object_class_install_property (object_class, PROP_CM_NAME,
293     g_param_spec_string ("connection-manager",
294       "connection-manager",
295       "The name of the connection manager this account uses",
296       NULL,
297       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
298
299   g_object_class_install_property (object_class, PROP_PROTOCOL,
300     g_param_spec_string ("protocol",
301       "Protocol",
302       "The name of the protocol this account uses",
303       NULL,
304       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
305
306   g_object_class_install_property (object_class, PROP_SERVICE,
307     g_param_spec_string ("service",
308       "Service",
309       "The service of this account, or NULL",
310       NULL,
311       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
312
313   g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
314     g_param_spec_string ("display-name",
315       "display-name",
316       "The display name account these settings belong to",
317       NULL,
318       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
319
320   g_object_class_install_property (object_class, PROP_DISPLAY_NAME_OVERRIDDEN,
321       g_param_spec_boolean ("display-name-overridden",
322         "display-name-overridden",
323         "Whether the display name for this account has been manually "
324         "overridden",
325         FALSE,
326         G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
327
328   g_object_class_install_property (object_class, PROP_READY,
329     g_param_spec_boolean ("ready",
330       "Ready",
331       "Whether this account is ready to be used",
332       FALSE,
333       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
334
335   signals[PASSWORD_RETRIEVED] =
336       g_signal_new ("password-retrieved",
337           G_TYPE_FROM_CLASS (empathy_account_settings_class),
338           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
339           g_cclosure_marshal_VOID__VOID,
340           G_TYPE_NONE, 0);
341 }
342
343 static void
344 empathy_account_settings_dispose (GObject *object)
345 {
346   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
347   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
348
349   if (priv->dispose_has_run)
350     return;
351
352   priv->dispose_has_run = TRUE;
353
354   if (priv->managers_ready_id != 0)
355     g_signal_handler_disconnect (priv->managers, priv->managers_ready_id);
356   priv->managers_ready_id = 0;
357
358   tp_clear_object (&priv->managers);
359   tp_clear_object (&priv->manager);
360   tp_clear_object (&priv->account_manager);
361   tp_clear_object (&priv->account);
362   tp_clear_object (&priv->protocol_obj);
363
364   /* release any references held by the object here */
365   if (G_OBJECT_CLASS (empathy_account_settings_parent_class)->dispose)
366     G_OBJECT_CLASS (empathy_account_settings_parent_class)->dispose (object);
367 }
368
369 static void
370 empathy_account_settings_free_unset_parameters (
371     EmpathyAccountSettings *settings)
372 {
373   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
374   guint i;
375
376   for (i = 0 ; i < priv->unset_parameters->len; i++)
377     g_free (g_array_index (priv->unset_parameters, gchar *, i));
378
379   g_array_set_size (priv->unset_parameters, 0);
380 }
381
382 static void
383 empathy_account_settings_finalize (GObject *object)
384 {
385   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
386   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
387   GList *l;
388
389   /* free any data held directly by the object here */
390   g_free (priv->cm_name);
391   g_free (priv->protocol);
392   g_free (priv->service);
393   g_free (priv->display_name);
394   g_free (priv->icon_name);
395   g_free (priv->password);
396   g_free (priv->password_original);
397
398   if (priv->required_params != NULL)
399     {
400       for (l = priv->required_params; l; l = l->next)
401         g_free (l->data);
402       g_list_free (priv->required_params);
403     }
404
405   g_hash_table_destroy (priv->parameters);
406   g_hash_table_destroy (priv->param_regexps);
407
408   empathy_account_settings_free_unset_parameters (self);
409   g_array_free (priv->unset_parameters, TRUE);
410
411   G_OBJECT_CLASS (empathy_account_settings_parent_class)->finalize (object);
412 }
413
414 static void
415 empathy_account_settings_protocol_obj_prepared_cb (GObject *source,
416     GAsyncResult *result,
417     gpointer user_data)
418 {
419   EmpathyAccountSettings *self = user_data;
420   GError *error = NULL;
421
422   if (!tp_proxy_prepare_finish (source, result, &error))
423     {
424       DEBUG ("Failed to prepare protocol object: %s", error->message);
425       g_clear_error (&error);
426       return;
427     }
428
429   empathy_account_settings_check_readyness (self);
430 }
431
432 static void
433 empathy_account_settings_get_password_cb (GObject *source,
434     GAsyncResult *result,
435     gpointer user_data)
436 {
437   EmpathyAccountSettings *self = user_data;
438   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
439   const gchar *password;
440   GError *error = NULL;
441
442   password = empathy_keyring_get_account_password_finish (TP_ACCOUNT (source),
443       result, &error);
444
445   if (error != NULL)
446     {
447       DEBUG ("Failed to get password: %s", error->message);
448       g_clear_error (&error);
449     }
450
451   /* It doesn't really matter if getting the password failed; that
452    * just means that it's not there, or let's act like that at
453    * least. */
454
455   g_assert (priv->password == NULL);
456
457   priv->password = g_strdup (password);
458   priv->password_original = g_strdup (password);
459
460   g_signal_emit (self, signals[PASSWORD_RETRIEVED], 0);
461 }
462
463 static void
464 empathy_account_settings_migrate_password_cb (GObject *source,
465     GAsyncResult *result,
466     gpointer user_data)
467 {
468   TpAccount *account = TP_ACCOUNT (source);
469   GError *error = NULL;
470   EmpathyAccountSettings *self = user_data;
471   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
472   GHashTable *empty;
473   const gchar *unset[] = { "password", NULL };
474
475   if (!empathy_keyring_set_account_password_finish (account, result, &error))
476     {
477       DEBUG ("Failed to set password: %s", error->message);
478       g_clear_error (&error);
479       return;
480     }
481
482   /* Now clear the password MC has stored. */
483   empty = tp_asv_new (NULL, NULL);
484   tp_account_update_parameters_async (priv->account,
485       empty, unset, NULL, NULL);
486
487   g_hash_table_remove (priv->parameters, "password");
488
489   g_hash_table_unref (empty);
490 }
491
492 static void
493 empathy_account_settings_try_migrating_password (EmpathyAccountSettings *self)
494 {
495   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
496   const GValue *v;
497   const gchar *password;
498
499   if (!priv->supports_sasl || empathy_account_settings_get (
500           self, "password") == NULL)
501     return;
502
503   /* mission-control still has our password, although the CM
504    * supports SASL. Let's try migrating it. */
505
506   DEBUG ("Trying to migrate password parameter from MC to the "
507       "keyring ourselves for account %s",
508       tp_account_get_path_suffix (priv->account));
509
510   v = empathy_account_settings_get (self, "password");
511
512   /* I can't imagine why this would fail. */
513   if (!G_VALUE_HOLDS_STRING (v))
514     return;
515
516   password = g_value_get_string (v);
517
518   if (EMP_STR_EMPTY (password))
519     return;
520
521   empathy_keyring_set_account_password_async (priv->account, password,
522       empathy_account_settings_migrate_password_cb, self);
523
524   /* We don't want to request the password again, we
525    * already know it. */
526   priv->password_requested = TRUE;
527
528   priv->password = g_strdup (password);
529   priv->password_original = g_strdup (password);
530 }
531
532 static void
533 empathy_account_settings_check_readyness (EmpathyAccountSettings *self)
534 {
535   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
536   const TpConnectionManagerProtocol *tp_protocol;
537   GQuark features[] = { TP_PROTOCOL_FEATURE_CORE, 0 };
538
539   if (priv->ready)
540     return;
541
542   if (priv->account != NULL
543       && !tp_account_is_prepared (priv->account, TP_ACCOUNT_FEATURE_CORE))
544       return;
545
546   if (!empathy_connection_managers_is_ready (priv->managers))
547     return;
548
549   if (priv->manager == NULL)
550     {
551       priv->manager = empathy_connection_managers_get_cm (
552           priv->managers, priv->cm_name);
553     }
554
555   if (priv->manager == NULL)
556     return;
557
558   g_object_ref (priv->manager);
559
560   if (priv->account != NULL)
561     {
562       g_free (priv->display_name);
563       priv->display_name =
564         g_strdup (tp_account_get_display_name (priv->account));
565
566       g_free (priv->icon_name);
567       priv->icon_name =
568         g_strdup (tp_account_get_icon_name (priv->account));
569
570       priv->uri_scheme_tel = empathy_account_has_uri_scheme_tel (priv->account);
571     }
572
573   tp_protocol = tp_connection_manager_get_protocol (priv->manager,
574     priv->protocol);
575
576   if (tp_protocol == NULL)
577     {
578       tp_clear_object (&priv->manager);
579       return;
580     }
581
582   if (priv->required_params == NULL)
583     {
584       TpConnectionManagerParam *cur;
585
586       for (cur = tp_protocol->params; cur->name != NULL; cur++)
587         {
588           if (tp_connection_manager_param_is_required (cur))
589             {
590               priv->required_params = g_list_append (priv->required_params,
591                                                      g_strdup (cur->name));
592             }
593         }
594     }
595
596   if (priv->protocol_obj == NULL)
597     {
598       priv->protocol_obj = g_object_ref (
599           tp_connection_manager_get_protocol_object (priv->manager,
600               priv->protocol));
601     }
602
603   if (!tp_proxy_is_prepared (priv->protocol_obj, TP_PROTOCOL_FEATURE_CORE)
604       && !priv->preparing_protocol)
605     {
606       priv->preparing_protocol = TRUE;
607       tp_proxy_prepare_async (priv->protocol_obj, features,
608           empathy_account_settings_protocol_obj_prepared_cb, self);
609       return;
610     }
611   else
612     {
613       if (tp_strv_contains (tp_protocol_get_authentication_types (
614                   priv->protocol_obj),
615               TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION))
616         {
617           priv->supports_sasl = TRUE;
618         }
619     }
620
621   /* NOTE: When removing MC migration code, remove this call, and the
622    * function it's calling. That's it. */
623   empathy_account_settings_try_migrating_password (self);
624
625   /* priv->account won't be a proper account if it's the account
626    * assistant showing this widget. */
627   if (priv->supports_sasl && !priv->password_requested
628       && priv->account != NULL)
629     {
630       priv->password_requested = TRUE;
631
632       /* Make this call but don't block on its readiness. We'll signal
633        * if it's updated later with ::password-retrieved. */
634       empathy_keyring_get_account_password_async (priv->account,
635           empathy_account_settings_get_password_cb, self);
636     }
637
638   priv->ready = TRUE;
639   g_object_notify (G_OBJECT (self), "ready");
640 }
641
642 static void
643 empathy_account_settings_account_ready_cb (GObject *source_object,
644     GAsyncResult *result,
645     gpointer user_data)
646 {
647   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
648   TpAccount *account = TP_ACCOUNT (source_object);
649   GError *error = NULL;
650
651   if (!tp_proxy_prepare_finish (account, result, &error))
652     {
653       DEBUG ("Failed to prepare account: %s", error->message);
654       g_error_free (error);
655       return;
656     }
657
658   empathy_account_settings_check_readyness (settings);
659 }
660
661 static void
662 empathy_account_settings_managers_ready_cb (GObject *object,
663     GParamSpec *pspec,
664     gpointer user_data)
665 {
666   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
667
668   empathy_account_settings_check_readyness (settings);
669 }
670
671 EmpathyAccountSettings *
672 empathy_account_settings_new (const gchar *connection_manager,
673     const gchar *protocol,
674     const gchar *service,
675     const char *display_name)
676 {
677   return g_object_new (EMPATHY_TYPE_ACCOUNT_SETTINGS,
678       "connection-manager", connection_manager,
679       "protocol", protocol,
680       "service", service,
681       "display-name", display_name,
682       NULL);
683 }
684
685 EmpathyAccountSettings *
686 empathy_account_settings_new_for_account (TpAccount *account)
687 {
688   return g_object_new (EMPATHY_TYPE_ACCOUNT_SETTINGS,
689       "account", account,
690       NULL);
691 }
692
693 TpConnectionManagerParam *
694 empathy_account_settings_get_tp_params (EmpathyAccountSettings *settings)
695 {
696   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
697   const TpConnectionManagerProtocol *tp_protocol;
698
699   g_return_val_if_fail (priv->manager != NULL, NULL);
700   g_return_val_if_fail (priv->protocol != NULL, NULL);
701
702   tp_protocol = tp_connection_manager_get_protocol (priv->manager,
703      priv->protocol);
704   if (tp_protocol == NULL)
705     {
706       DEBUG ("Can't retrieve TpConnectionManagerProtocol for protocol '%s'",
707           priv->protocol);
708       return NULL;
709     }
710
711   return tp_protocol->params;
712 }
713
714 gboolean
715 empathy_account_settings_is_ready (EmpathyAccountSettings *settings)
716 {
717   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
718
719   return priv->ready;
720 }
721
722 const gchar *
723 empathy_account_settings_get_cm (EmpathyAccountSettings *settings)
724 {
725   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
726
727   return priv->cm_name;
728 }
729
730 const gchar *
731 empathy_account_settings_get_protocol (EmpathyAccountSettings *settings)
732 {
733   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
734
735   return priv->protocol;
736 }
737
738 const gchar *
739 empathy_account_settings_get_service (EmpathyAccountSettings *settings)
740 {
741   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
742
743   return priv->service;
744 }
745
746 gchar *
747 empathy_account_settings_get_icon_name (EmpathyAccountSettings *settings)
748 {
749   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
750
751   return priv->icon_name;
752 }
753
754 const gchar *
755 empathy_account_settings_get_display_name (EmpathyAccountSettings *settings)
756 {
757   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
758
759   return priv->display_name;
760 }
761
762 TpAccount *
763 empathy_account_settings_get_account (EmpathyAccountSettings *settings)
764 {
765   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
766
767   return priv->account;
768 }
769
770 static gboolean
771 empathy_account_settings_is_unset (EmpathyAccountSettings *settings,
772     const gchar *param)
773 {
774   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
775   GArray *a;
776   guint i;
777
778   a = priv->unset_parameters;
779
780   for (i = 0; i < a->len; i++)
781     {
782       if (!tp_strdiff (g_array_index (a, gchar *, i), param))
783         return TRUE;
784     }
785
786   return FALSE;
787 }
788
789 static TpConnectionManagerParam *
790 empathy_account_settings_get_tp_param (EmpathyAccountSettings *settings,
791     const gchar *param)
792 {
793   TpConnectionManagerParam *tp_params =
794       empathy_account_settings_get_tp_params (settings);
795   TpConnectionManagerParam *p;
796
797   for (p = tp_params; p != NULL && p->name != NULL; p++)
798     {
799       if (tp_strdiff (p->name, param))
800         continue;
801
802       return p;
803     }
804
805   return NULL;
806 }
807
808 gboolean
809 empathy_account_settings_have_tp_param (EmpathyAccountSettings *settings,
810     const gchar *param)
811 {
812   return (empathy_account_settings_get_tp_param (settings, param) != NULL);
813 }
814
815 static void
816 account_settings_remove_from_unset (EmpathyAccountSettings *settings,
817     const gchar *param)
818 {
819   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
820   guint idx;
821   gchar *val;
822
823   for (idx = 0; idx < priv->unset_parameters->len; idx++)
824     {
825       val = g_array_index (priv->unset_parameters, gchar *, idx);
826
827       if (!tp_strdiff (val, param))
828         {
829           priv->unset_parameters =
830             g_array_remove_index (priv->unset_parameters, idx);
831           g_free (val);
832
833           break;
834         }
835     }
836 }
837
838 const GValue *
839 empathy_account_settings_get_default (EmpathyAccountSettings *settings,
840     const gchar *param)
841 {
842   TpConnectionManagerParam *p;
843
844   p = empathy_account_settings_get_tp_param (settings, param);
845
846   if (p == NULL || !(p->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT))
847     return NULL;
848
849   return &(p->default_value);
850 }
851
852 const gchar *
853 empathy_account_settings_get_dbus_signature (EmpathyAccountSettings *settings,
854     const gchar *param)
855 {
856   TpConnectionManagerParam *p;
857
858   p = empathy_account_settings_get_tp_param (settings, param);
859
860   if (p == NULL)
861     return NULL;
862
863   return p->dbus_signature;
864 }
865
866 const GValue *
867 empathy_account_settings_get (EmpathyAccountSettings *settings,
868     const gchar *param)
869 {
870   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
871   const GValue *result = NULL;
872
873   /* Lookup the update parameters we set */
874   result = tp_asv_lookup (priv->parameters, param);
875   if (result != NULL)
876     return result;
877
878   /* If the parameters isn't unset use the accounts setting if any */
879   if (priv->account != NULL
880       && !empathy_account_settings_is_unset (settings, param))
881     {
882       const GHashTable *parameters;
883
884       parameters = tp_account_get_parameters (priv->account);
885       result = tp_asv_lookup (parameters, param);
886
887       if (result != NULL)
888         return result;
889     }
890
891   /* fallback to the default */
892   return empathy_account_settings_get_default (settings, param);
893 }
894
895 void
896 empathy_account_settings_unset (EmpathyAccountSettings *settings,
897     const gchar *param)
898 {
899   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
900   gchar *v;
901   if (empathy_account_settings_is_unset (settings, param))
902     return;
903
904   if (priv->supports_sasl && !tp_strdiff (param, "password"))
905     {
906       g_free (priv->password);
907       priv->password = NULL;
908       priv->password_changed = TRUE;
909       return;
910     }
911
912   v = g_strdup (param);
913
914   g_array_append_val (priv->unset_parameters, v);
915   g_hash_table_remove (priv->parameters, param);
916 }
917
918 void
919 empathy_account_settings_discard_changes (EmpathyAccountSettings *settings)
920 {
921   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
922
923   g_hash_table_remove_all (priv->parameters);
924   empathy_account_settings_free_unset_parameters (settings);
925
926   priv->password_changed = FALSE;
927   g_free (priv->password);
928   priv->password = g_strdup (priv->password_original);
929
930   if (priv->account != NULL)
931     priv->uri_scheme_tel = empathy_account_has_uri_scheme_tel (priv->account);
932   else
933     priv->uri_scheme_tel = FALSE;
934 }
935
936 const gchar *
937 empathy_account_settings_get_string (EmpathyAccountSettings *settings,
938     const gchar *param)
939 {
940   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
941   const GValue *v;
942
943   if (!tp_strdiff (param, "password") && priv->supports_sasl)
944     {
945       return priv->password;
946     }
947
948   v = empathy_account_settings_get (settings, param);
949
950   if (v == NULL || !G_VALUE_HOLDS_STRING (v))
951     return NULL;
952
953   return g_value_get_string (v);
954 }
955
956 const gchar * const *
957 empathy_account_settings_get_strv (EmpathyAccountSettings *settings,
958     const gchar *param)
959 {
960   const GValue *v;
961
962   v = empathy_account_settings_get (settings, param);
963
964   if (v == NULL || !G_VALUE_HOLDS (v, G_TYPE_STRV))
965     return NULL;
966
967   return g_value_get_boxed (v);
968 }
969
970 gint32
971 empathy_account_settings_get_int32 (EmpathyAccountSettings *settings,
972     const gchar *param)
973 {
974   const GValue *v;
975   gint32 ret = 0;
976
977   v = empathy_account_settings_get (settings, param);
978
979   if (v == NULL)
980     return 0;
981
982   switch G_VALUE_TYPE (v)
983     {
984       case G_TYPE_UCHAR:
985         ret = g_value_get_uchar (v);
986         break;
987       case G_TYPE_INT:
988         ret = g_value_get_int (v);
989         break;
990       case G_TYPE_UINT:
991         ret = CLAMP (g_value_get_uint (v), (guint) G_MININT32,
992             G_MAXINT32);
993         break;
994       case G_TYPE_INT64:
995         ret = CLAMP (g_value_get_int64 (v), G_MININT32, G_MAXINT32);
996         break;
997       case G_TYPE_UINT64:
998         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT32,
999             G_MAXINT32);
1000         break;
1001       default:
1002         ret = 0;
1003         break;
1004     }
1005
1006   return ret;
1007 }
1008
1009 gint64
1010 empathy_account_settings_get_int64 (EmpathyAccountSettings *settings,
1011     const gchar *param)
1012 {
1013   const GValue *v;
1014   gint64 ret = 0;
1015
1016   v = empathy_account_settings_get (settings, param);
1017   if (v == NULL)
1018     return 0;
1019
1020   switch G_VALUE_TYPE (v)
1021     {
1022       case G_TYPE_UCHAR:
1023         ret = g_value_get_uchar (v);
1024         break;
1025       case G_TYPE_INT:
1026         ret = g_value_get_int (v);
1027         break;
1028       case G_TYPE_UINT:
1029         ret = g_value_get_uint (v);
1030         break;
1031       case G_TYPE_INT64:
1032         ret = g_value_get_int64 (v);
1033         break;
1034       case G_TYPE_UINT64:
1035         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT64, G_MAXINT64);
1036         break;
1037       default:
1038         ret = 0;
1039         break;
1040     }
1041
1042   return ret;
1043 }
1044
1045 guint32
1046 empathy_account_settings_get_uint32 (EmpathyAccountSettings *settings,
1047     const gchar *param)
1048 {
1049   const GValue *v;
1050   guint32 ret;
1051
1052   v = empathy_account_settings_get (settings, param);
1053   if (v == NULL)
1054     return 0;
1055
1056   switch G_VALUE_TYPE (v)
1057     {
1058       case G_TYPE_UCHAR:
1059         ret = g_value_get_uchar (v);
1060         break;
1061       case G_TYPE_INT:
1062         ret = MAX (0, g_value_get_int (v));
1063         break;
1064       case G_TYPE_UINT:
1065         ret = g_value_get_uint (v);
1066         break;
1067       case G_TYPE_INT64:
1068         ret = CLAMP (g_value_get_int64 (v), 0, G_MAXUINT32);
1069         break;
1070       case G_TYPE_UINT64:
1071         ret = MIN (g_value_get_uint64 (v), G_MAXUINT32);
1072         break;
1073       default:
1074         ret = 0;
1075         break;
1076     }
1077
1078   return ret;
1079 }
1080
1081 guint64
1082 empathy_account_settings_get_uint64 (EmpathyAccountSettings *settings,
1083     const gchar *param)
1084 {
1085   const GValue *v;
1086   guint64 ret = 0;
1087
1088   v = empathy_account_settings_get (settings, param);
1089
1090   if (v == NULL || !G_VALUE_HOLDS_INT (v))
1091     return 0;
1092
1093   switch G_VALUE_TYPE (v)
1094     {
1095       case G_TYPE_UCHAR:
1096         ret = g_value_get_uchar (v);
1097         break;
1098       case G_TYPE_INT:
1099         ret = MAX (0, g_value_get_int (v));
1100         break;
1101       case G_TYPE_UINT:
1102         ret = g_value_get_uint (v);
1103         break;
1104       case G_TYPE_INT64:
1105         ret = MAX (0, g_value_get_int64 (v));
1106         break;
1107       case G_TYPE_UINT64:
1108         ret = g_value_get_uint64 (v);
1109         break;
1110       default:
1111         ret = 0;
1112         break;
1113     }
1114
1115   return ret;
1116 }
1117
1118 gboolean
1119 empathy_account_settings_get_boolean (EmpathyAccountSettings *settings,
1120     const gchar *param)
1121 {
1122   const GValue *v;
1123
1124   v = empathy_account_settings_get (settings, param);
1125
1126   if (v == NULL || !G_VALUE_HOLDS_BOOLEAN (v))
1127     return FALSE;
1128
1129   return g_value_get_boolean (v);
1130 }
1131
1132 void
1133 empathy_account_settings_set_string (EmpathyAccountSettings *settings,
1134     const gchar *param,
1135     const gchar *value)
1136 {
1137   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1138
1139   g_return_if_fail (param != NULL);
1140   g_return_if_fail (value != NULL);
1141
1142   if (!tp_strdiff (param, "password") && priv->supports_sasl)
1143     {
1144       g_free (priv->password);
1145       priv->password = g_strdup (value);
1146       priv->password_changed = TRUE;
1147     }
1148   else
1149     {
1150       tp_asv_set_string (priv->parameters, g_strdup (param), value);
1151     }
1152
1153   account_settings_remove_from_unset (settings, param);
1154 }
1155
1156 void
1157 empathy_account_settings_set_strv (EmpathyAccountSettings *settings,
1158     const gchar *param,
1159     gchar **value)
1160 {
1161   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1162
1163   g_return_if_fail (param != NULL);
1164   g_return_if_fail (value != NULL);
1165
1166   tp_asv_set_strv (priv->parameters, g_strdup (param), value);
1167
1168   account_settings_remove_from_unset (settings, param);
1169 }
1170
1171 void
1172 empathy_account_settings_set_int32 (EmpathyAccountSettings *settings,
1173     const gchar *param,
1174     gint32 value)
1175 {
1176   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1177
1178   g_return_if_fail (param != NULL);
1179
1180   tp_asv_set_int32 (priv->parameters, g_strdup (param), value);
1181
1182   account_settings_remove_from_unset (settings, param);
1183 }
1184
1185 void
1186 empathy_account_settings_set_int64 (EmpathyAccountSettings *settings,
1187     const gchar *param,
1188     gint64 value)
1189 {
1190   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1191
1192   g_return_if_fail (param != NULL);
1193
1194   tp_asv_set_int64 (priv->parameters, g_strdup (param), value);
1195
1196   account_settings_remove_from_unset (settings, param);
1197 }
1198
1199 void
1200 empathy_account_settings_set_uint32 (EmpathyAccountSettings *settings,
1201     const gchar *param,
1202     guint32 value)
1203 {
1204   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1205
1206   g_return_if_fail (param != NULL);
1207
1208   tp_asv_set_uint32 (priv->parameters, g_strdup (param), value);
1209
1210   account_settings_remove_from_unset (settings, param);
1211 }
1212
1213 void
1214 empathy_account_settings_set_uint64 (EmpathyAccountSettings *settings,
1215     const gchar *param,
1216     guint64 value)
1217 {
1218   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1219
1220   g_return_if_fail (param != NULL);
1221
1222   tp_asv_set_uint64 (priv->parameters, g_strdup (param), value);
1223
1224   account_settings_remove_from_unset (settings, param);
1225 }
1226
1227 void
1228 empathy_account_settings_set_boolean (EmpathyAccountSettings *settings,
1229     const gchar *param,
1230     gboolean value)
1231 {
1232   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1233
1234   g_return_if_fail (param != NULL);
1235
1236   tp_asv_set_boolean (priv->parameters, g_strdup (param), value);
1237
1238   account_settings_remove_from_unset (settings, param);
1239 }
1240
1241 static void
1242 account_settings_display_name_set_cb (GObject *src,
1243     GAsyncResult *res,
1244     gpointer user_data)
1245 {
1246   GError *error = NULL;
1247   TpAccount *account = TP_ACCOUNT (src);
1248   GSimpleAsyncResult *set_result = user_data;
1249
1250   tp_account_set_display_name_finish (account, res, &error);
1251
1252   if (error != NULL)
1253     {
1254       g_simple_async_result_set_from_error (set_result, error);
1255       g_error_free (error);
1256     }
1257
1258   g_simple_async_result_complete (set_result);
1259   g_object_unref (set_result);
1260 }
1261
1262 void
1263 empathy_account_settings_set_display_name_async (
1264   EmpathyAccountSettings *settings,
1265   const gchar *name,
1266   GAsyncReadyCallback callback,
1267   gpointer user_data)
1268 {
1269   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1270   GSimpleAsyncResult *result;
1271
1272   g_return_if_fail (name != NULL);
1273
1274   result = g_simple_async_result_new (G_OBJECT (settings),
1275       callback, user_data, empathy_account_settings_set_display_name_finish);
1276
1277   if (!tp_strdiff (name, priv->display_name))
1278     {
1279       /* Nothing to do */
1280       g_simple_async_result_complete_in_idle (result);
1281       return;
1282     }
1283
1284   if (priv->account == NULL)
1285     {
1286       if (priv->display_name != NULL)
1287         g_free (priv->display_name);
1288
1289       priv->display_name = g_strdup (name);
1290
1291       g_simple_async_result_complete_in_idle (result);
1292
1293       return;
1294     }
1295
1296   tp_account_set_display_name_async (priv->account, name,
1297       account_settings_display_name_set_cb, result);
1298 }
1299
1300 gboolean
1301 empathy_account_settings_set_display_name_finish (
1302   EmpathyAccountSettings *settings,
1303   GAsyncResult *result,
1304   GError **error)
1305 {
1306   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1307       error))
1308     return FALSE;
1309
1310   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1311     G_OBJECT (settings), empathy_account_settings_set_display_name_finish),
1312       FALSE);
1313
1314   return TRUE;
1315 }
1316
1317 static void
1318 account_settings_icon_name_set_cb (GObject *src,
1319     GAsyncResult *res,
1320     gpointer user_data)
1321 {
1322   GError *error = NULL;
1323   TpAccount *account = TP_ACCOUNT (src);
1324   GSimpleAsyncResult *set_result = user_data;
1325
1326   tp_account_set_icon_name_finish (account, res, &error);
1327
1328   if (error != NULL)
1329     {
1330       g_simple_async_result_set_from_error (set_result, error);
1331       g_error_free (error);
1332     }
1333
1334   g_simple_async_result_complete (set_result);
1335   g_object_unref (set_result);
1336 }
1337
1338 void
1339 empathy_account_settings_set_icon_name_async (
1340   EmpathyAccountSettings *settings,
1341   const gchar *name,
1342   GAsyncReadyCallback callback,
1343   gpointer user_data)
1344 {
1345   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1346   GSimpleAsyncResult *result;
1347
1348   g_return_if_fail (name != NULL);
1349
1350   result = g_simple_async_result_new (G_OBJECT (settings),
1351       callback, user_data, empathy_account_settings_set_icon_name_finish);
1352
1353   if (priv->account == NULL)
1354     {
1355       if (priv->icon_name != NULL)
1356         g_free (priv->icon_name);
1357
1358       priv->icon_name = g_strdup (name);
1359
1360       g_simple_async_result_complete_in_idle (result);
1361
1362       return;
1363     }
1364
1365   tp_account_set_icon_name_async (priv->account, name,
1366       account_settings_icon_name_set_cb, result);
1367 }
1368
1369 gboolean
1370 empathy_account_settings_set_icon_name_finish (
1371   EmpathyAccountSettings *settings,
1372   GAsyncResult *result,
1373   GError **error)
1374 {
1375   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1376       error))
1377     return FALSE;
1378
1379   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1380     G_OBJECT (settings), empathy_account_settings_set_icon_name_finish),
1381       FALSE);
1382
1383   return TRUE;
1384 }
1385
1386 static void
1387 empathy_account_settings_processed_password (GObject *source,
1388     GAsyncResult *result,
1389     gpointer user_data,
1390     gpointer finish_func)
1391 {
1392   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1393   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1394   GSimpleAsyncResult *r;
1395   GError *error = NULL;
1396   gboolean (*func) (TpAccount *source, GAsyncResult *result, GError **error) =
1397     finish_func;
1398
1399   g_free (priv->password_original);
1400   priv->password_original = g_strdup (priv->password);
1401
1402   if (!func (TP_ACCOUNT (source), result, &error))
1403     {
1404       g_simple_async_result_set_from_error (priv->apply_result, error);
1405       g_error_free (error);
1406     }
1407
1408   empathy_account_settings_discard_changes (settings);
1409
1410   r = priv->apply_result;
1411   priv->apply_result = NULL;
1412
1413   g_simple_async_result_complete (r);
1414   g_object_unref (r);
1415 }
1416
1417 static void
1418 empathy_account_settings_set_password_cb (GObject *source,
1419     GAsyncResult *result,
1420     gpointer user_data)
1421 {
1422   empathy_account_settings_processed_password (source, result, user_data,
1423       empathy_keyring_set_account_password_finish);
1424 }
1425
1426 static void
1427 empathy_account_settings_delete_password_cb (GObject *source,
1428     GAsyncResult *result,
1429     gpointer user_data)
1430 {
1431   empathy_account_settings_processed_password (source, result, user_data,
1432       empathy_keyring_delete_account_password_finish);
1433 }
1434
1435 static void
1436 update_account_uri_schemes (EmpathyAccountSettings *self)
1437 {
1438   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1439
1440   if (priv->uri_scheme_tel == empathy_account_has_uri_scheme_tel (
1441         priv->account))
1442     return;
1443
1444   tp_account_set_uri_scheme_association_async (priv->account, "tel",
1445       priv->uri_scheme_tel, NULL, NULL);
1446 }
1447
1448 static void
1449 empathy_account_settings_account_updated (GObject *source,
1450     GAsyncResult *result,
1451     gpointer user_data)
1452 {
1453   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1454   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1455   GSimpleAsyncResult *r;
1456   GError *error = NULL;
1457   GStrv reconnect_required = NULL;
1458
1459   if (!tp_account_update_parameters_finish (TP_ACCOUNT (source),
1460           result, &reconnect_required, &error))
1461     {
1462       g_simple_async_result_set_from_error (priv->apply_result, error);
1463       g_error_free (error);
1464       goto out;
1465     }
1466
1467   /* Only set the password in the keyring if the CM supports SASL and
1468    * it's changed. */
1469   if (priv->supports_sasl && priv->password_changed)
1470     {
1471       if (priv->password != NULL)
1472         {
1473           /* FIXME: we shouldn't save the password if we
1474            * can't (MaySaveResponse=False) but we don't have API to check that
1475            * at this point (fdo #35382). */
1476           empathy_keyring_set_account_password_async (priv->account, priv->password,
1477               empathy_account_settings_set_password_cb, settings);
1478         }
1479       else
1480         {
1481           empathy_keyring_delete_account_password_async (priv->account,
1482               empathy_account_settings_delete_password_cb, settings);
1483         }
1484
1485       return;
1486     }
1487
1488   update_account_uri_schemes (settings);
1489
1490   g_simple_async_result_set_op_res_gboolean (priv->apply_result,
1491       g_strv_length (reconnect_required) > 0);
1492
1493 out:
1494   empathy_account_settings_discard_changes (settings);
1495
1496   r = priv->apply_result;
1497   priv->apply_result = NULL;
1498
1499   g_simple_async_result_complete (r);
1500   g_object_unref (r);
1501   g_strfreev (reconnect_required);
1502 }
1503
1504 static void
1505 empathy_account_settings_created_cb (GObject *source,
1506     GAsyncResult *result,
1507     gpointer user_data)
1508 {
1509   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1510   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1511   TpAccount *account;
1512   GError *error = NULL;
1513   GSimpleAsyncResult *r;
1514
1515   account = tp_account_manager_create_account_finish (
1516     TP_ACCOUNT_MANAGER (source), result, &error);
1517
1518   if (account == NULL)
1519     {
1520       g_simple_async_result_set_from_error (priv->apply_result, error);
1521     }
1522   else
1523     {
1524       priv->account = g_object_ref (account);
1525
1526       if (priv->supports_sasl && priv->password != NULL)
1527         {
1528           /* Save the password before connecting */
1529           /* FIXME: we shouldn't save the password if we
1530            * can't (MaySaveResponse=False) but we don't have API to check that
1531            * at this point (fdo #35382). */
1532           empathy_keyring_set_account_password_async (priv->account,
1533               priv->password, empathy_account_settings_set_password_cb,
1534               settings);
1535           return;
1536         }
1537
1538       update_account_uri_schemes (settings);
1539
1540       empathy_account_settings_discard_changes (settings);
1541     }
1542
1543   r = priv->apply_result;
1544   priv->apply_result = NULL;
1545
1546   g_simple_async_result_complete (r);
1547   g_object_unref (r);
1548 }
1549
1550
1551 static void
1552 empathy_account_settings_do_create_account (EmpathyAccountSettings *settings)
1553 {
1554   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1555   GHashTable *properties;
1556   TpConnectionPresenceType type;
1557   gchar *status;
1558   gchar *message;
1559   EmpathyPresenceManager *presence_mgr;
1560
1561   properties = tp_asv_new (NULL, NULL);
1562
1563   presence_mgr = empathy_presence_manager_dup_singleton ();
1564   type = empathy_presence_manager_get_requested_presence (presence_mgr, &status,
1565       &message);
1566   g_object_unref (presence_mgr);
1567
1568   if (type != TP_CONNECTION_PRESENCE_TYPE_UNSET)
1569     {
1570       /* Create the account with the requested presence the same as the current
1571         * global requested presence, but don't enable it */
1572       GValueArray *presence;
1573       GValue vtype = { 0, };
1574       GValue vstatus = { 0, };
1575       GValue vmessage = { 0, };
1576
1577       presence = g_value_array_new (3);
1578
1579       g_value_init (&vtype, G_TYPE_UINT);
1580       g_value_set_uint (&vtype, type);
1581       g_value_array_append (presence, &vtype);
1582
1583       g_value_init (&vstatus, G_TYPE_STRING);
1584       g_value_take_string (&vstatus, status);
1585       g_value_array_append (presence, &vstatus);
1586
1587       g_value_init (&vmessage, G_TYPE_STRING);
1588       g_value_take_string (&vmessage, message);
1589       g_value_array_append (presence, &vmessage);
1590
1591       tp_asv_take_boxed (properties, TP_IFACE_ACCOUNT ".RequestedPresence",
1592         TP_STRUCT_TYPE_SIMPLE_PRESENCE, presence);
1593     }
1594
1595   tp_asv_set_string (properties, TP_IFACE_ACCOUNT ".Icon",
1596       priv->icon_name);
1597
1598   if (priv->service != NULL)
1599     tp_asv_set_string (properties, TP_PROP_ACCOUNT_SERVICE, priv->service);
1600
1601   tp_account_manager_create_account_async (priv->account_manager,
1602     priv->cm_name, priv->protocol, priv->display_name,
1603     priv->parameters, properties,
1604     empathy_account_settings_created_cb,
1605     settings);
1606
1607   g_hash_table_unref (properties);
1608 }
1609
1610 static void
1611 empathy_account_settings_manager_ready_cb (GObject *source_object,
1612     GAsyncResult *result,
1613     gpointer user_data)
1614 {
1615   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1616   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1617   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1618   GError *error = NULL;
1619
1620   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1621     {
1622       DEBUG ("Failed to prepare account manager: %s", error->message);
1623       g_error_free (error);
1624       return;
1625     }
1626
1627   g_assert (priv->apply_result != NULL && priv->account == NULL);
1628   empathy_account_settings_do_create_account (settings);
1629 }
1630
1631 void
1632 empathy_account_settings_apply_async (EmpathyAccountSettings *settings,
1633     GAsyncReadyCallback callback,
1634     gpointer user_data)
1635 {
1636   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1637
1638   if (priv->apply_result != NULL)
1639     {
1640       g_simple_async_report_error_in_idle (G_OBJECT (settings),
1641           callback, user_data,
1642           G_IO_ERROR, G_IO_ERROR_PENDING, "Applying already in progress");
1643       return;
1644     }
1645
1646   priv->apply_result = g_simple_async_result_new (G_OBJECT (settings),
1647       callback, user_data, empathy_account_settings_apply_finish);
1648
1649   /* We'll have to reconnect only if we change none DBus_Property on an
1650    * existing account. */
1651   g_simple_async_result_set_op_res_gboolean (priv->apply_result, FALSE);
1652
1653   if (priv->account == NULL)
1654     {
1655       tp_account_manager_prepare_async (priv->account_manager, NULL,
1656           empathy_account_settings_manager_ready_cb, settings);
1657     }
1658   else
1659     {
1660       tp_account_update_parameters_async (priv->account,
1661           priv->parameters, (const gchar **)priv->unset_parameters->data,
1662           empathy_account_settings_account_updated, settings);
1663     }
1664 }
1665
1666 gboolean
1667 empathy_account_settings_apply_finish (EmpathyAccountSettings *settings,
1668     GAsyncResult *result,
1669     gboolean *reconnect_required,
1670     GError **error)
1671 {
1672   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1673       error))
1674     return FALSE;
1675
1676   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1677     G_OBJECT (settings), empathy_account_settings_apply_finish), FALSE);
1678
1679   if (reconnect_required != NULL)
1680     *reconnect_required = g_simple_async_result_get_op_res_gboolean (
1681         G_SIMPLE_ASYNC_RESULT (result));
1682
1683   return TRUE;
1684 }
1685
1686 gboolean
1687 empathy_account_settings_has_account (EmpathyAccountSettings *settings,
1688     TpAccount *account)
1689 {
1690   EmpathyAccountSettingsPriv *priv;
1691   const gchar *account_path;
1692   const gchar *priv_account_path;
1693
1694   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1695   g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
1696
1697   priv = GET_PRIV (settings);
1698
1699   if (priv->account == NULL)
1700     return FALSE;
1701
1702   account_path = tp_proxy_get_object_path (TP_PROXY (account));
1703   priv_account_path = tp_proxy_get_object_path (TP_PROXY (priv->account));
1704
1705   return (!tp_strdiff (account_path, priv_account_path));
1706 }
1707
1708 void
1709 empathy_account_settings_set_regex (EmpathyAccountSettings *settings,
1710     const gchar *param,
1711     const gchar *pattern)
1712 {
1713   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1714   GRegex *regex;
1715   GError *error = NULL;
1716
1717   regex = g_regex_new (pattern, 0, 0, &error);
1718   if (regex == NULL)
1719     {
1720       g_warning ("Failed to create reg exp: %s", error->message);
1721       g_error_free (error);
1722       return;
1723     }
1724
1725   g_hash_table_insert (priv->param_regexps, g_strdup (param), regex);
1726 }
1727
1728 gboolean
1729 empathy_account_settings_parameter_is_valid (
1730     EmpathyAccountSettings *settings,
1731     const gchar *param)
1732 {
1733   EmpathyAccountSettingsPriv *priv;
1734   const GRegex *regex;
1735   const gchar *value;
1736
1737   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1738
1739   priv = GET_PRIV (settings);
1740
1741   if (g_list_find_custom (priv->required_params, param, (GCompareFunc) strcmp))
1742     {
1743       /* first, look if it's set in our own parameters */
1744       if (tp_asv_lookup (priv->parameters, param))
1745         goto test_regex;
1746
1747       /* if we did not unset the parameter, look if it's in the account */
1748       if (priv->account != NULL &&
1749           !empathy_account_settings_is_unset (settings, param))
1750         {
1751           const GHashTable *account_params;
1752
1753           account_params = tp_account_get_parameters (priv->account);
1754           if (tp_asv_lookup (account_params, param))
1755             goto test_regex;
1756         }
1757
1758       return FALSE;
1759     }
1760
1761 test_regex:
1762   /* test whether parameter value matches its regex */
1763   regex = g_hash_table_lookup (priv->param_regexps, param);
1764   if (regex)
1765     {
1766       value = empathy_account_settings_get_string (settings, param);
1767       if (value != NULL && !g_regex_match (regex, value, 0, NULL))
1768         return FALSE;
1769     }
1770
1771   return TRUE;
1772 }
1773
1774 gboolean
1775 empathy_account_settings_is_valid (EmpathyAccountSettings *settings)
1776 {
1777   EmpathyAccountSettingsPriv *priv;
1778   const gchar *param;
1779   GHashTableIter iter;
1780   GList *l;
1781
1782   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1783
1784   priv = GET_PRIV (settings);
1785
1786   for (l = priv->required_params; l; l = l->next)
1787     {
1788       if (!empathy_account_settings_parameter_is_valid (settings, l->data))
1789         return FALSE;
1790     }
1791
1792   g_hash_table_iter_init (&iter, priv->param_regexps);
1793   while (g_hash_table_iter_next (&iter, (gpointer *) &param, NULL))
1794     {
1795       if (!empathy_account_settings_parameter_is_valid (settings, param))
1796         return FALSE;
1797     }
1798
1799   return TRUE;
1800 }
1801
1802 const TpConnectionManagerProtocol *
1803 empathy_account_settings_get_tp_protocol (EmpathyAccountSettings *self)
1804 {
1805   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1806
1807   return tp_connection_manager_get_protocol (priv->manager, priv->protocol);
1808 }
1809
1810 gboolean
1811 empathy_account_settings_supports_sasl (EmpathyAccountSettings *self)
1812 {
1813   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1814
1815   return priv->supports_sasl;
1816 }
1817
1818 gboolean
1819 empathy_account_settings_param_is_supported (EmpathyAccountSettings *self,
1820     const gchar *param)
1821 {
1822   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1823
1824   return tp_protocol_has_param (priv->protocol_obj, param);
1825 }
1826
1827 void
1828 empathy_account_settings_set_uri_scheme_tel (EmpathyAccountSettings *self,
1829     gboolean associate)
1830 {
1831   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1832
1833   priv->uri_scheme_tel = associate;
1834 }
1835
1836 gboolean
1837 empathy_account_settings_has_uri_scheme_tel (
1838     EmpathyAccountSettings *self)
1839 {
1840   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1841
1842   return priv->uri_scheme_tel;
1843 }