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