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