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