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