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