]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account-settings.c
29ff3f1b786d0698d35641e6d397363498a32e3a
[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-utils.h"
33 #include "empathy-idle.h"
34
35 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
36 #include <libempathy/empathy-debug.h>
37
38 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountSettings)
39
40 G_DEFINE_TYPE(EmpathyAccountSettings, empathy_account_settings, G_TYPE_OBJECT)
41
42 enum {
43   PROP_ACCOUNT = 1,
44   PROP_CM_NAME,
45   PROP_PROTOCOL,
46   PROP_SERVICE,
47   PROP_DISPLAY_NAME,
48   PROP_DISPLAY_NAME_OVERRIDDEN,
49   PROP_READY
50 };
51
52 /* private structure */
53 typedef struct _EmpathyAccountSettingsPriv EmpathyAccountSettingsPriv;
54
55 struct _EmpathyAccountSettingsPriv
56 {
57   gboolean dispose_has_run;
58   EmpathyConnectionManagers *managers;
59   TpAccountManager *account_manager;
60
61   TpConnectionManager *manager;
62
63   TpAccount *account;
64   gchar *cm_name;
65   gchar *protocol;
66   gchar *service;
67   gchar *display_name;
68   gchar *icon_name;
69   gboolean display_name_overridden;
70   gboolean ready;
71
72   GHashTable *parameters;
73   GArray *unset_parameters;
74   GArray *required_params;
75
76   gulong managers_ready_id;
77
78   GSimpleAsyncResult *apply_result;
79 };
80
81 static void
82 empathy_account_settings_init (EmpathyAccountSettings *obj)
83 {
84   EmpathyAccountSettingsPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE ((obj),
85     EMPATHY_TYPE_ACCOUNT_SETTINGS, EmpathyAccountSettingsPriv);
86
87   obj->priv = priv;
88
89   /* allocate any data required by the object here */
90   priv->managers = empathy_connection_managers_dup_singleton ();
91   priv->account_manager = tp_account_manager_dup ();
92
93   priv->parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
94     g_free, (GDestroyNotify) tp_g_value_slice_free);
95
96   priv->unset_parameters = g_array_new (TRUE, FALSE, sizeof (gchar *));
97 }
98
99 static void empathy_account_settings_dispose (GObject *object);
100 static void empathy_account_settings_finalize (GObject *object);
101 static void empathy_account_settings_account_ready_cb (GObject *source_object,
102     GAsyncResult *result, gpointer user_data);
103 static void empathy_account_settings_managers_ready_cb (GObject *obj,
104     GParamSpec *pspec, gpointer user_data);
105 static void empathy_account_settings_check_readyness (
106     EmpathyAccountSettings *self);
107
108 static void
109 empathy_account_settings_set_property (GObject *object,
110     guint prop_id,
111     const GValue *value,
112     GParamSpec *pspec)
113 {
114   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (object);
115   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
116
117   switch (prop_id)
118     {
119       case PROP_ACCOUNT:
120         priv->account = g_value_dup_object (value);
121         break;
122       case PROP_CM_NAME:
123         priv->cm_name = g_value_dup_string (value);
124         break;
125       case PROP_PROTOCOL:
126         priv->protocol = g_value_dup_string (value);
127         break;
128       case PROP_SERVICE:
129         priv->service = g_value_dup_string (value);
130         break;
131       case PROP_DISPLAY_NAME:
132         priv->display_name = g_value_dup_string (value);
133         break;
134       case PROP_DISPLAY_NAME_OVERRIDDEN:
135         priv->display_name_overridden = g_value_get_boolean (value);
136         break;
137       default:
138         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
139         break;
140     }
141 }
142
143 static void
144 empathy_account_settings_get_property (GObject *object,
145     guint prop_id,
146     GValue *value,
147     GParamSpec *pspec)
148 {
149   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (object);
150   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
151
152   switch (prop_id)
153     {
154       case PROP_ACCOUNT:
155         g_value_set_object (value, priv->account);
156         break;
157       case PROP_CM_NAME:
158         g_value_set_string (value, priv->cm_name);
159         break;
160       case PROP_PROTOCOL:
161         g_value_set_string (value, priv->protocol);
162         break;
163       case PROP_SERVICE:
164         g_value_set_string (value, priv->service);
165         break;
166       case PROP_DISPLAY_NAME:
167         g_value_set_string (value, priv->display_name);
168         break;
169       case PROP_DISPLAY_NAME_OVERRIDDEN:
170         g_value_set_boolean (value, priv->display_name_overridden);
171         break;
172       case PROP_READY:
173         g_value_set_boolean (value, priv->ready);
174         break;
175       default:
176         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177         break;
178     }
179 }
180
181 static void
182 empathy_account_settings_constructed (GObject *object)
183 {
184   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
185   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
186
187   if (priv->account != NULL)
188     {
189       g_free (priv->cm_name);
190       g_free (priv->protocol);
191       g_free (priv->service);
192
193       priv->cm_name =
194         g_strdup (tp_account_get_connection_manager (priv->account));
195       priv->protocol =
196         g_strdup (tp_account_get_protocol (priv->account));
197       priv->service =
198         g_strdup (tp_account_get_service (priv->account));
199       priv->icon_name = g_strdup
200         (tp_account_get_icon_name (priv->account));
201     }
202   else
203     {
204       priv->icon_name = empathy_protocol_icon_name (priv->protocol);
205     }
206
207   g_assert (priv->cm_name != NULL && priv->protocol != NULL);
208
209   empathy_account_settings_check_readyness (self);
210
211   if (!priv->ready)
212     {
213       tp_account_prepare_async (priv->account, NULL,
214           empathy_account_settings_account_ready_cb, self);
215       tp_g_signal_connect_object (priv->managers, "notify::ready",
216         G_CALLBACK (empathy_account_settings_managers_ready_cb), object, 0);
217     }
218
219   if (G_OBJECT_CLASS (
220         empathy_account_settings_parent_class)->constructed != NULL)
221     G_OBJECT_CLASS (
222         empathy_account_settings_parent_class)->constructed (object);
223 }
224
225
226 static void
227 empathy_account_settings_class_init (
228     EmpathyAccountSettingsClass *empathy_account_settings_class)
229 {
230   GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_settings_class);
231
232   g_type_class_add_private (empathy_account_settings_class, sizeof
233       (EmpathyAccountSettingsPriv));
234
235   object_class->dispose = empathy_account_settings_dispose;
236   object_class->finalize = empathy_account_settings_finalize;
237   object_class->set_property = empathy_account_settings_set_property;
238   object_class->get_property = empathy_account_settings_get_property;
239   object_class->constructed = empathy_account_settings_constructed;
240
241   g_object_class_install_property (object_class, PROP_ACCOUNT,
242     g_param_spec_object ("account",
243       "Account",
244       "The TpAccount backing these settings",
245       TP_TYPE_ACCOUNT,
246       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
247
248   g_object_class_install_property (object_class, PROP_CM_NAME,
249     g_param_spec_string ("connection-manager",
250       "connection-manager",
251       "The name of the connection manager this account uses",
252       NULL,
253       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
254
255   g_object_class_install_property (object_class, PROP_PROTOCOL,
256     g_param_spec_string ("protocol",
257       "Protocol",
258       "The name of the protocol this account uses",
259       NULL,
260       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
261
262   g_object_class_install_property (object_class, PROP_SERVICE,
263     g_param_spec_string ("service",
264       "Service",
265       "The service of this account, or NULL",
266       NULL,
267       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
268
269   g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
270     g_param_spec_string ("display-name",
271       "display-name",
272       "The display name account these settings belong to",
273       NULL,
274       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
275
276   g_object_class_install_property (object_class, PROP_DISPLAY_NAME_OVERRIDDEN,
277       g_param_spec_boolean ("display-name-overridden",
278         "display-name-overridden",
279         "Whether the display name for this account has been manually "
280         "overridden",
281         FALSE,
282         G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
283
284   g_object_class_install_property (object_class, PROP_READY,
285     g_param_spec_boolean ("ready",
286       "Ready",
287       "Whether this account is ready to be used",
288       FALSE,
289       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
290 }
291
292 static void
293 empathy_account_settings_dispose (GObject *object)
294 {
295   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
296   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
297
298   if (priv->dispose_has_run)
299     return;
300
301   priv->dispose_has_run = TRUE;
302
303   if (priv->managers_ready_id != 0)
304     g_signal_handler_disconnect (priv->managers, priv->managers_ready_id);
305   priv->managers_ready_id = 0;
306
307   if (priv->managers != NULL)
308     g_object_unref (priv->managers);
309   priv->managers = NULL;
310
311   if (priv->manager != NULL)
312     g_object_unref (priv->manager);
313   priv->manager = NULL;
314
315   if (priv->account_manager != NULL)
316     g_object_unref (priv->account_manager);
317   priv->account_manager = NULL;
318
319   if (priv->account != NULL)
320     g_object_unref (priv->account);
321   priv->account = NULL;
322
323   /* release any references held by the object here */
324   if (G_OBJECT_CLASS (empathy_account_settings_parent_class)->dispose)
325     G_OBJECT_CLASS (empathy_account_settings_parent_class)->dispose (object);
326 }
327
328 static void
329 empathy_account_settings_free_unset_parameters (
330     EmpathyAccountSettings *settings)
331 {
332   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
333   guint i;
334
335   for (i = 0 ; i < priv->unset_parameters->len; i++)
336     g_free (g_array_index (priv->unset_parameters, gchar *, i));
337
338   g_array_set_size (priv->unset_parameters, 0);
339 }
340
341 static void
342 empathy_account_settings_finalize (GObject *object)
343 {
344   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
345   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
346
347   /* free any data held directly by the object here */
348   g_free (priv->cm_name);
349   g_free (priv->protocol);
350   g_free (priv->service);
351   g_free (priv->display_name);
352   g_free (priv->icon_name);
353
354   if (priv->required_params != NULL)
355     g_array_free (priv->required_params, TRUE);
356
357   g_hash_table_destroy (priv->parameters);
358
359   empathy_account_settings_free_unset_parameters (self);
360   g_array_free (priv->unset_parameters, TRUE);
361
362   G_OBJECT_CLASS (empathy_account_settings_parent_class)->finalize (object);
363 }
364
365 static void
366 empathy_account_settings_check_readyness (EmpathyAccountSettings *self)
367 {
368   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
369   const TpConnectionManagerProtocol *tp_protocol;
370
371   if (priv->ready)
372     return;
373
374   if (priv->account != NULL
375       && !tp_account_is_prepared (priv->account, TP_ACCOUNT_FEATURE_CORE))
376       return;
377
378   if (!empathy_connection_managers_is_ready (priv->managers))
379     return;
380
381   priv->manager = empathy_connection_managers_get_cm (
382     priv->managers, priv->cm_name);
383
384   if (priv->manager == NULL)
385     return;
386
387   if (priv->account != NULL)
388     {
389       g_free (priv->display_name);
390       priv->display_name =
391         g_strdup (tp_account_get_display_name (priv->account));
392
393       g_free (priv->icon_name);
394       priv->icon_name =
395         g_strdup (tp_account_get_icon_name (priv->account));
396     }
397
398   tp_protocol = tp_connection_manager_get_protocol (priv->manager,
399     priv->protocol);
400
401   if (tp_protocol == NULL)
402     {
403       priv->manager = NULL;
404       return;
405     }
406
407   if (priv->required_params == NULL)
408     {
409       TpConnectionManagerParam *cur;
410       char *val;
411
412       priv->required_params = g_array_new (TRUE, FALSE, sizeof (gchar *));
413
414       for (cur = tp_protocol->params; cur->name != NULL; cur++)
415         {
416           if (tp_connection_manager_param_is_required (cur))
417             {
418               val = g_strdup (cur->name);
419               g_array_append_val (priv->required_params, val);
420             }
421         }
422     }
423
424   g_object_ref (priv->manager);
425
426   priv->ready = TRUE;
427   g_object_notify (G_OBJECT (self), "ready");
428 }
429
430 static void
431 empathy_account_settings_account_ready_cb (GObject *source_object,
432     GAsyncResult *result,
433     gpointer user_data)
434 {
435   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
436   TpAccount *account = TP_ACCOUNT (source_object);
437   GError *error = NULL;
438
439   if (!tp_account_prepare_finish (account, result, &error))
440     {
441       DEBUG ("Failed to prepare account: %s", error->message);
442       g_error_free (error);
443       return;
444     }
445
446   empathy_account_settings_check_readyness (settings);
447 }
448
449 static void
450 empathy_account_settings_managers_ready_cb (GObject *object,
451     GParamSpec *pspec,
452     gpointer user_data)
453 {
454   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
455
456   empathy_account_settings_check_readyness (settings);
457 }
458
459 EmpathyAccountSettings *
460 empathy_account_settings_new (const gchar *connection_manager,
461     const gchar *protocol,
462     const gchar *service,
463     const char *display_name)
464 {
465   return g_object_new (EMPATHY_TYPE_ACCOUNT_SETTINGS,
466       "connection-manager", connection_manager,
467       "protocol", protocol,
468       "service", service,
469       "display-name", display_name,
470       NULL);
471 }
472
473 EmpathyAccountSettings *
474 empathy_account_settings_new_for_account (TpAccount *account)
475 {
476   return g_object_new (EMPATHY_TYPE_ACCOUNT_SETTINGS,
477       "account", account,
478       NULL);
479 }
480
481 TpConnectionManagerParam *
482 empathy_account_settings_get_tp_params (EmpathyAccountSettings *settings)
483 {
484   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
485   const TpConnectionManagerProtocol *tp_protocol;
486
487   g_return_val_if_fail (priv->manager != NULL, NULL);
488   g_return_val_if_fail (priv->protocol != NULL, NULL);
489
490   tp_protocol = tp_connection_manager_get_protocol (priv->manager,
491      priv->protocol);
492   if (tp_protocol == NULL)
493     {
494       DEBUG ("Can't retrieve TpConnectionManagerProtocol for protocol '%s'",
495           priv->protocol);
496       return NULL;
497     }
498
499   return tp_protocol->params;
500 }
501
502 gboolean
503 empathy_account_settings_is_ready (EmpathyAccountSettings *settings)
504 {
505   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
506
507   return priv->ready;
508 }
509
510 const gchar *
511 empathy_account_settings_get_cm (EmpathyAccountSettings *settings)
512 {
513   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
514
515   return priv->cm_name;
516 }
517
518 const gchar *
519 empathy_account_settings_get_protocol (EmpathyAccountSettings *settings)
520 {
521   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
522
523   return priv->protocol;
524 }
525
526 const gchar *
527 empathy_account_settings_get_service (EmpathyAccountSettings *settings)
528 {
529   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
530
531   return priv->service;
532 }
533
534 gchar *
535 empathy_account_settings_get_icon_name (EmpathyAccountSettings *settings)
536 {
537   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
538
539   return priv->icon_name;
540 }
541
542 const gchar *
543 empathy_account_settings_get_display_name (EmpathyAccountSettings *settings)
544 {
545   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
546
547   return priv->display_name;
548 }
549
550 TpAccount *
551 empathy_account_settings_get_account (EmpathyAccountSettings *settings)
552 {
553   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
554
555   return priv->account;
556 }
557
558 static gboolean
559 empathy_account_settings_is_unset (EmpathyAccountSettings *settings,
560     const gchar *param)
561 {
562   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
563   GArray *a;
564   guint i;
565
566   a = priv->unset_parameters;
567
568   for (i = 0; i < a->len; i++)
569     {
570       if (!tp_strdiff (g_array_index (a, gchar *, i), param))
571         return TRUE;
572     }
573
574   return FALSE;
575 }
576
577 static TpConnectionManagerParam *
578 empathy_account_settings_get_tp_param (EmpathyAccountSettings *settings,
579     const gchar *param)
580 {
581   TpConnectionManagerParam *tp_params =
582       empathy_account_settings_get_tp_params (settings);
583   TpConnectionManagerParam *p;
584
585   for (p = tp_params; p != NULL && p->name != NULL; p++)
586     {
587       if (tp_strdiff (p->name, param))
588         continue;
589
590       return p;
591     }
592
593   return NULL;
594 }
595
596 static void
597 account_settings_remove_from_unset (EmpathyAccountSettings *settings,
598     const gchar *param)
599 {
600   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
601   guint idx;
602   gchar *val;
603
604   for (idx = 0; idx < priv->unset_parameters->len; idx++)
605     {
606       val = g_array_index (priv->unset_parameters, gchar *, idx);
607
608       if (!tp_strdiff (val, param))
609         {
610           priv->unset_parameters =
611             g_array_remove_index (priv->unset_parameters, idx);
612           g_free (val);
613
614           break;
615         }
616     }
617 }
618
619 const GValue *
620 empathy_account_settings_get_default (EmpathyAccountSettings *settings,
621     const gchar *param)
622 {
623   TpConnectionManagerParam *p;
624
625   p = empathy_account_settings_get_tp_param (settings, param);
626
627   if (p == NULL || !(p->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT))
628     return NULL;
629
630   return &(p->default_value);
631 }
632
633 const gchar *
634 empathy_account_settings_get_dbus_signature (EmpathyAccountSettings *settings,
635     const gchar *param)
636 {
637   TpConnectionManagerParam *p;
638
639   p = empathy_account_settings_get_tp_param (settings, param);
640
641   if (p == NULL)
642     return NULL;
643
644   return p->dbus_signature;
645 }
646
647 const GValue *
648 empathy_account_settings_get (EmpathyAccountSettings *settings,
649     const gchar *param)
650 {
651   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
652   const GValue *result = NULL;
653
654   /* Lookup the update parameters we set */
655   result = tp_asv_lookup (priv->parameters, param);
656   if (result != NULL)
657     return result;
658
659   /* If the parameters isn't unset use the accounts setting if any */
660   if (priv->account != NULL
661       && !empathy_account_settings_is_unset (settings, param))
662     {
663       const GHashTable *parameters;
664
665       parameters = tp_account_get_parameters (priv->account);
666       result = tp_asv_lookup (parameters, param);
667
668       if (result != NULL)
669         return result;
670     }
671
672   /* fallback to the default */
673   return empathy_account_settings_get_default (settings, param);
674 }
675
676 void
677 empathy_account_settings_unset (EmpathyAccountSettings *settings,
678     const gchar *param)
679 {
680   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
681   gchar *v;
682   if (empathy_account_settings_is_unset (settings, param))
683     return;
684
685   v = g_strdup (param);
686
687   g_array_append_val (priv->unset_parameters, v);
688   g_hash_table_remove (priv->parameters, param);
689 }
690
691 void
692 empathy_account_settings_discard_changes (EmpathyAccountSettings *settings)
693 {
694   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
695
696   g_hash_table_remove_all (priv->parameters);
697   empathy_account_settings_free_unset_parameters (settings);
698 }
699
700 const gchar *
701 empathy_account_settings_get_string (EmpathyAccountSettings *settings,
702     const gchar *param)
703 {
704   const GValue *v;
705
706   v = empathy_account_settings_get (settings, param);
707
708   if (v == NULL || !G_VALUE_HOLDS_STRING (v))
709     return NULL;
710
711   return g_value_get_string (v);
712 }
713
714 const gchar * const *
715 empathy_account_settings_get_strv (EmpathyAccountSettings *settings,
716     const gchar *param)
717 {
718   const GValue *v;
719
720   v = empathy_account_settings_get (settings, param);
721
722   if (v == NULL || !G_VALUE_HOLDS (v, G_TYPE_STRV))
723     return NULL;
724
725   return g_value_get_boxed (v);
726 }
727
728 gint32
729 empathy_account_settings_get_int32 (EmpathyAccountSettings *settings,
730     const gchar *param)
731 {
732   const GValue *v;
733   gint32 ret = 0;
734
735   v = empathy_account_settings_get (settings, param);
736
737   if (v == NULL)
738     return 0;
739
740   switch G_VALUE_TYPE (v)
741     {
742       case G_TYPE_UCHAR:
743         ret = g_value_get_uchar (v);
744         break;
745       case G_TYPE_INT:
746         ret = g_value_get_int (v);
747         break;
748       case G_TYPE_UINT:
749         ret = CLAMP (g_value_get_uint (v), (guint) G_MININT32,
750             G_MAXINT32);
751         break;
752       case G_TYPE_INT64:
753         ret = CLAMP (g_value_get_int64 (v), G_MININT32, G_MAXINT32);
754         break;
755       case G_TYPE_UINT64:
756         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT32,
757             G_MAXINT32);
758         break;
759       default:
760         ret = 0;
761         break;
762     }
763
764   return ret;
765 }
766
767 gint64
768 empathy_account_settings_get_int64 (EmpathyAccountSettings *settings,
769     const gchar *param)
770 {
771   const GValue *v;
772   gint64 ret = 0;
773
774   v = empathy_account_settings_get (settings, param);
775   if (v == NULL)
776     return 0;
777
778   switch G_VALUE_TYPE (v)
779     {
780       case G_TYPE_UCHAR:
781         ret = g_value_get_uchar (v);
782         break;
783       case G_TYPE_INT:
784         ret = g_value_get_int (v);
785         break;
786       case G_TYPE_UINT:
787         ret = g_value_get_uint (v);
788         break;
789       case G_TYPE_INT64:
790         ret = g_value_get_int64 (v);
791         break;
792       case G_TYPE_UINT64:
793         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT64, G_MAXINT64);
794         break;
795       default:
796         ret = 0;
797         break;
798     }
799
800   return ret;
801 }
802
803 guint32
804 empathy_account_settings_get_uint32 (EmpathyAccountSettings *settings,
805     const gchar *param)
806 {
807   const GValue *v;
808   guint32 ret;
809
810   v = empathy_account_settings_get (settings, param);
811   if (v == NULL)
812     return 0;
813
814   switch G_VALUE_TYPE (v)
815     {
816       case G_TYPE_UCHAR:
817         ret = g_value_get_uchar (v);
818         break;
819       case G_TYPE_INT:
820         ret = MAX (0, g_value_get_int (v));
821         break;
822       case G_TYPE_UINT:
823         ret = g_value_get_uint (v);
824         break;
825       case G_TYPE_INT64:
826         ret = CLAMP (g_value_get_int64 (v), 0, G_MAXUINT32);
827         break;
828       case G_TYPE_UINT64:
829         ret = MIN (g_value_get_uint64 (v), G_MAXUINT32);
830         break;
831       default:
832         ret = 0;
833         break;
834     }
835
836   return ret;
837 }
838
839 guint64
840 empathy_account_settings_get_uint64 (EmpathyAccountSettings *settings,
841     const gchar *param)
842 {
843   const GValue *v;
844   guint64 ret = 0;
845
846   v = empathy_account_settings_get (settings, param);
847
848   if (v == NULL || !G_VALUE_HOLDS_INT (v))
849     return 0;
850
851   switch G_VALUE_TYPE (v)
852     {
853       case G_TYPE_UCHAR:
854         ret = g_value_get_uchar (v);
855         break;
856       case G_TYPE_INT:
857         ret = MAX (0, g_value_get_int (v));
858         break;
859       case G_TYPE_UINT:
860         ret = g_value_get_uint (v);
861         break;
862       case G_TYPE_INT64:
863         ret = MAX (0, g_value_get_int64 (v));
864         break;
865       case G_TYPE_UINT64:
866         ret = g_value_get_uint64 (v);
867         break;
868       default:
869         ret = 0;
870         break;
871     }
872
873   return ret;
874 }
875
876 gboolean
877 empathy_account_settings_get_boolean (EmpathyAccountSettings *settings,
878     const gchar *param)
879 {
880   const GValue *v;
881
882   v = empathy_account_settings_get (settings, param);
883
884   if (v == NULL || !G_VALUE_HOLDS_BOOLEAN (v))
885     return FALSE;
886
887   return g_value_get_boolean (v);
888 }
889
890 void
891 empathy_account_settings_set_string (EmpathyAccountSettings *settings,
892     const gchar *param,
893     const gchar *value)
894 {
895   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
896
897   g_return_if_fail (param != NULL);
898   g_return_if_fail (value != NULL);
899
900   tp_asv_set_string (priv->parameters, g_strdup (param), value);
901
902   account_settings_remove_from_unset (settings, param);
903 }
904
905 void
906 empathy_account_settings_set_strv (EmpathyAccountSettings *settings,
907     const gchar *param,
908     gchar **value)
909 {
910   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
911
912   g_return_if_fail (param != NULL);
913   g_return_if_fail (value != NULL);
914
915   tp_asv_set_strv (priv->parameters, g_strdup (param), value);
916
917   account_settings_remove_from_unset (settings, param);
918 }
919
920 void
921 empathy_account_settings_set_int32 (EmpathyAccountSettings *settings,
922     const gchar *param,
923     gint32 value)
924 {
925   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
926
927   g_return_if_fail (param != NULL);
928
929   tp_asv_set_int32 (priv->parameters, g_strdup (param), value);
930
931   account_settings_remove_from_unset (settings, param);
932 }
933
934 void
935 empathy_account_settings_set_int64 (EmpathyAccountSettings *settings,
936     const gchar *param,
937     gint64 value)
938 {
939   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
940
941   g_return_if_fail (param != NULL);
942
943   tp_asv_set_int64 (priv->parameters, g_strdup (param), value);
944
945   account_settings_remove_from_unset (settings, param);
946 }
947
948 void
949 empathy_account_settings_set_uint32 (EmpathyAccountSettings *settings,
950     const gchar *param,
951     guint32 value)
952 {
953   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
954
955   g_return_if_fail (param != NULL);
956
957   tp_asv_set_uint32 (priv->parameters, g_strdup (param), value);
958
959   account_settings_remove_from_unset (settings, param);
960 }
961
962 void
963 empathy_account_settings_set_uint64 (EmpathyAccountSettings *settings,
964     const gchar *param,
965     guint64 value)
966 {
967   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
968
969   g_return_if_fail (param != NULL);
970
971   tp_asv_set_uint64 (priv->parameters, g_strdup (param), value);
972
973   account_settings_remove_from_unset (settings, param);
974 }
975
976 void
977 empathy_account_settings_set_boolean (EmpathyAccountSettings *settings,
978     const gchar *param,
979     gboolean value)
980 {
981   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
982
983   g_return_if_fail (param != NULL);
984
985   tp_asv_set_boolean (priv->parameters, g_strdup (param), value);
986
987   account_settings_remove_from_unset (settings, param);
988 }
989
990 static void
991 account_settings_display_name_set_cb (GObject *src,
992     GAsyncResult *res,
993     gpointer user_data)
994 {
995   GError *error = NULL;
996   TpAccount *account = TP_ACCOUNT (src);
997   GSimpleAsyncResult *set_result = user_data;
998
999   tp_account_set_display_name_finish (account, res, &error);
1000
1001   if (error != NULL)
1002     {
1003       g_simple_async_result_set_from_error (set_result, error);
1004       g_error_free (error);
1005     }
1006
1007   g_simple_async_result_complete (set_result);
1008   g_object_unref (set_result);
1009 }
1010
1011 void
1012 empathy_account_settings_set_display_name_async (
1013   EmpathyAccountSettings *settings,
1014   const gchar *name,
1015   GAsyncReadyCallback callback,
1016   gpointer user_data)
1017 {
1018   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1019   GSimpleAsyncResult *result;
1020
1021   g_return_if_fail (name != NULL);
1022
1023   result = g_simple_async_result_new (G_OBJECT (settings),
1024       callback, user_data, empathy_account_settings_set_display_name_finish);
1025
1026   if (!tp_strdiff (name, priv->display_name))
1027     {
1028       /* Nothing to do */
1029       g_simple_async_result_complete_in_idle (result);
1030       return;
1031     }
1032
1033   if (priv->account == NULL)
1034     {
1035       if (priv->display_name != NULL)
1036         g_free (priv->display_name);
1037
1038       priv->display_name = g_strdup (name);
1039
1040       g_simple_async_result_complete_in_idle (result);
1041
1042       return;
1043     }
1044
1045   tp_account_set_display_name_async (priv->account, name,
1046       account_settings_display_name_set_cb, result);
1047 }
1048
1049 gboolean
1050 empathy_account_settings_set_display_name_finish (
1051   EmpathyAccountSettings *settings,
1052   GAsyncResult *result,
1053   GError **error)
1054 {
1055   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1056       error))
1057     return FALSE;
1058
1059   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1060     G_OBJECT (settings), empathy_account_settings_set_display_name_finish),
1061       FALSE);
1062
1063   return TRUE;
1064 }
1065
1066 static void
1067 account_settings_icon_name_set_cb (GObject *src,
1068     GAsyncResult *res,
1069     gpointer user_data)
1070 {
1071   GError *error = NULL;
1072   TpAccount *account = TP_ACCOUNT (src);
1073   GSimpleAsyncResult *set_result = user_data;
1074
1075   tp_account_set_icon_name_finish (account, res, &error);
1076
1077   if (error != NULL)
1078     {
1079       g_simple_async_result_set_from_error (set_result, error);
1080       g_error_free (error);
1081     }
1082
1083   g_simple_async_result_complete (set_result);
1084   g_object_unref (set_result);
1085 }
1086
1087 void
1088 empathy_account_settings_set_icon_name_async (
1089   EmpathyAccountSettings *settings,
1090   const gchar *name,
1091   GAsyncReadyCallback callback,
1092   gpointer user_data)
1093 {
1094   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1095   GSimpleAsyncResult *result;
1096
1097   g_return_if_fail (name != NULL);
1098
1099   result = g_simple_async_result_new (G_OBJECT (settings),
1100       callback, user_data, empathy_account_settings_set_icon_name_finish);
1101
1102   if (priv->account == NULL)
1103     {
1104       if (priv->icon_name != NULL)
1105         g_free (priv->icon_name);
1106
1107       priv->icon_name = g_strdup (name);
1108
1109       g_simple_async_result_complete_in_idle (result);
1110
1111       return;
1112     }
1113
1114   tp_account_set_icon_name_async (priv->account, name,
1115       account_settings_icon_name_set_cb, result);
1116 }
1117
1118 gboolean
1119 empathy_account_settings_set_icon_name_finish (
1120   EmpathyAccountSettings *settings,
1121   GAsyncResult *result,
1122   GError **error)
1123 {
1124   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1125       error))
1126     return FALSE;
1127
1128   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1129     G_OBJECT (settings), empathy_account_settings_set_icon_name_finish),
1130       FALSE);
1131
1132   return TRUE;
1133 }
1134
1135 static void
1136 empathy_account_settings_account_updated (GObject *source,
1137     GAsyncResult *result,
1138     gpointer user_data)
1139 {
1140   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1141   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1142   GSimpleAsyncResult *r;
1143   GError *error = NULL;
1144
1145   if (!tp_account_update_parameters_finish (TP_ACCOUNT (source),
1146           result, NULL, &error))
1147     {
1148       g_simple_async_result_set_from_error (priv->apply_result, error);
1149       g_error_free (error);
1150     }
1151   else
1152     {
1153       empathy_account_settings_discard_changes (settings);
1154     }
1155
1156   r = priv->apply_result;
1157   priv->apply_result = NULL;
1158
1159   g_simple_async_result_complete (r);
1160   g_object_unref (r);
1161 }
1162
1163 static void
1164 empathy_account_settings_created_cb (GObject *source,
1165     GAsyncResult *result,
1166     gpointer user_data)
1167 {
1168   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1169   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1170   TpAccount *account;
1171   GError *error = NULL;
1172   GSimpleAsyncResult *r;
1173
1174   account = tp_account_manager_create_account_finish (
1175     TP_ACCOUNT_MANAGER (source), result, &error);
1176
1177   if (account == NULL)
1178     {
1179       g_simple_async_result_set_from_error (priv->apply_result, error);
1180     }
1181   else
1182     {
1183       priv->account = g_object_ref (account);
1184       empathy_account_settings_discard_changes (settings);
1185     }
1186
1187   r = priv->apply_result;
1188   priv->apply_result = NULL;
1189
1190   g_simple_async_result_complete (r);
1191   g_object_unref (r);
1192 }
1193
1194
1195 static void
1196 empathy_account_settings_do_create_account (EmpathyAccountSettings *settings)
1197 {
1198   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1199   GHashTable *properties;
1200   TpConnectionPresenceType type;
1201   gchar *status;
1202   gchar *message;
1203   EmpathyIdle *idle;
1204
1205   properties = tp_asv_new (NULL, NULL);
1206
1207   idle = empathy_idle_dup_singleton ();
1208   type = empathy_idle_get_requested_presence (idle, &status, &message);
1209   g_object_unref (idle);
1210
1211   if (type != TP_CONNECTION_PRESENCE_TYPE_UNSET)
1212     {
1213       /* Create the account with the requested presence the same as the current
1214         * global requested presence, but don't enable it */
1215       GValueArray *presence;
1216       GValue vtype = { 0, };
1217       GValue vstatus = { 0, };
1218       GValue vmessage = { 0, };
1219
1220       presence = g_value_array_new (3);
1221
1222       g_value_init (&vtype, G_TYPE_UINT);
1223       g_value_set_uint (&vtype, type);
1224       g_value_array_append (presence, &vtype);
1225
1226       g_value_init (&vstatus, G_TYPE_STRING);
1227       g_value_take_string (&vstatus, status);
1228       g_value_array_append (presence, &vstatus);
1229
1230       g_value_init (&vmessage, G_TYPE_STRING);
1231       g_value_take_string (&vmessage, message);
1232       g_value_array_append (presence, &vmessage);
1233
1234       tp_asv_take_boxed (properties, TP_IFACE_ACCOUNT ".RequestedPresence",
1235         TP_STRUCT_TYPE_SIMPLE_PRESENCE, presence);
1236     }
1237
1238   tp_asv_set_string (properties, TP_IFACE_ACCOUNT ".Icon",
1239       priv->icon_name);
1240
1241   if (priv->service != NULL)
1242     tp_asv_set_string (properties, TP_PROP_ACCOUNT_SERVICE, priv->service);
1243
1244   tp_account_manager_create_account_async (priv->account_manager,
1245     priv->cm_name, priv->protocol, priv->display_name,
1246     priv->parameters, properties,
1247     empathy_account_settings_created_cb,
1248     settings);
1249
1250   g_hash_table_unref (properties);
1251 }
1252
1253 static void
1254 empathy_account_settings_manager_ready_cb (GObject *source_object,
1255     GAsyncResult *result,
1256     gpointer user_data)
1257 {
1258   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1259   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1260   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1261   GError *error = NULL;
1262
1263   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1264     {
1265       DEBUG ("Failed to prepare account manager: %s", error->message);
1266       g_error_free (error);
1267       return;
1268     }
1269
1270   g_assert (priv->apply_result != NULL && priv->account == NULL);
1271   empathy_account_settings_do_create_account (settings);
1272 }
1273
1274 void
1275 empathy_account_settings_apply_async (EmpathyAccountSettings *settings,
1276     GAsyncReadyCallback callback,
1277     gpointer user_data)
1278 {
1279   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1280
1281   if (priv->apply_result != NULL)
1282     {
1283       g_simple_async_report_error_in_idle (G_OBJECT (settings),
1284           callback, user_data,
1285           G_IO_ERROR, G_IO_ERROR_PENDING, "Applying already in progress");
1286       return;
1287     }
1288
1289   priv->apply_result = g_simple_async_result_new (G_OBJECT (settings),
1290       callback, user_data, empathy_account_settings_apply_finish);
1291
1292   if (priv->account == NULL)
1293     {
1294       tp_account_manager_prepare_async (priv->account_manager, NULL,
1295           empathy_account_settings_manager_ready_cb, settings);
1296     }
1297   else
1298     {
1299       tp_account_update_parameters_async (priv->account,
1300           priv->parameters, (const gchar **)priv->unset_parameters->data,
1301           empathy_account_settings_account_updated, settings);
1302     }
1303 }
1304
1305 gboolean
1306 empathy_account_settings_apply_finish (EmpathyAccountSettings *settings,
1307     GAsyncResult *result,
1308     GError **error)
1309 {
1310   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1311       error))
1312     return FALSE;
1313
1314   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1315     G_OBJECT (settings), empathy_account_settings_apply_finish), FALSE);
1316
1317   return TRUE;
1318 }
1319
1320 gboolean
1321 empathy_account_settings_has_account (EmpathyAccountSettings *settings,
1322     TpAccount *account)
1323 {
1324   EmpathyAccountSettingsPriv *priv;
1325   const gchar *account_path;
1326   const gchar *priv_account_path;
1327
1328   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1329   g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
1330
1331   priv = GET_PRIV (settings);
1332
1333   if (priv->account == NULL)
1334     return FALSE;
1335
1336   account_path = tp_proxy_get_object_path (TP_PROXY (account));
1337   priv_account_path = tp_proxy_get_object_path (TP_PROXY (priv->account));
1338
1339   return (!tp_strdiff (account_path, priv_account_path));
1340 }
1341
1342 gboolean
1343 empathy_account_settings_is_valid (EmpathyAccountSettings *settings)
1344 {
1345   EmpathyAccountSettingsPriv *priv;
1346   guint idx;
1347   gchar *current;
1348   gboolean missed = FALSE;
1349
1350   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1351
1352   priv = GET_PRIV (settings);
1353
1354   for (idx = 0; idx < priv->required_params->len; idx++)
1355     {
1356       current = g_array_index (priv->required_params, gchar *, idx);
1357
1358       /* first, look if it's set in our own parameters */
1359       if (tp_asv_lookup (priv->parameters, current))
1360         continue;
1361
1362       /* if we did not unset the parameter, look if it's in the account */
1363       if (priv->account != NULL &&
1364           !empathy_account_settings_is_unset (settings, current))
1365         {
1366           const GHashTable *account_params;
1367
1368           account_params = tp_account_get_parameters (priv->account);
1369           if (tp_asv_lookup (account_params, current))
1370             continue;
1371         }
1372
1373       missed = TRUE;
1374       break;
1375     }
1376
1377   return !missed;
1378 }
1379
1380 const TpConnectionManagerProtocol *
1381 empathy_account_settings_get_tp_protocol (EmpathyAccountSettings *self)
1382 {
1383   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1384
1385   return tp_connection_manager_get_protocol (priv->manager, priv->protocol);
1386 }