]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account-settings.c
use TP_PROP_*
[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   tp_asv_set_string (priv->parameters, g_strdup (param), value);
898
899   account_settings_remove_from_unset (settings, param);
900 }
901
902 void
903 empathy_account_settings_set_strv (EmpathyAccountSettings *settings,
904     const gchar *param,
905     gchar **value)
906 {
907   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
908
909   tp_asv_set_strv (priv->parameters, g_strdup (param), value);
910
911   account_settings_remove_from_unset (settings, param);
912 }
913
914 void
915 empathy_account_settings_set_int32 (EmpathyAccountSettings *settings,
916     const gchar *param,
917     gint32 value)
918 {
919   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
920
921   tp_asv_set_int32 (priv->parameters, g_strdup (param), value);
922
923   account_settings_remove_from_unset (settings, param);
924 }
925
926 void
927 empathy_account_settings_set_int64 (EmpathyAccountSettings *settings,
928     const gchar *param,
929     gint64 value)
930 {
931   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
932
933   tp_asv_set_int64 (priv->parameters, g_strdup (param), value);
934
935   account_settings_remove_from_unset (settings, param);
936 }
937
938 void
939 empathy_account_settings_set_uint32 (EmpathyAccountSettings *settings,
940     const gchar *param,
941     guint32 value)
942 {
943   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
944
945   tp_asv_set_uint32 (priv->parameters, g_strdup (param), value);
946
947   account_settings_remove_from_unset (settings, param);
948 }
949
950 void
951 empathy_account_settings_set_uint64 (EmpathyAccountSettings *settings,
952     const gchar *param,
953     guint64 value)
954 {
955   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
956
957   tp_asv_set_uint64 (priv->parameters, g_strdup (param), value);
958
959   account_settings_remove_from_unset (settings, param);
960 }
961
962 void
963 empathy_account_settings_set_boolean (EmpathyAccountSettings *settings,
964     const gchar *param,
965     gboolean value)
966 {
967   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
968
969   tp_asv_set_boolean (priv->parameters, g_strdup (param), value);
970
971   account_settings_remove_from_unset (settings, param);
972 }
973
974 static void
975 account_settings_display_name_set_cb (GObject *src,
976     GAsyncResult *res,
977     gpointer user_data)
978 {
979   GError *error = NULL;
980   TpAccount *account = TP_ACCOUNT (src);
981   GSimpleAsyncResult *set_result = user_data;
982
983   tp_account_set_display_name_finish (account, res, &error);
984
985   if (error != NULL)
986     {
987       g_simple_async_result_set_from_error (set_result, error);
988       g_error_free (error);
989     }
990
991   g_simple_async_result_complete (set_result);
992   g_object_unref (set_result);
993 }
994
995 void
996 empathy_account_settings_set_display_name_async (
997   EmpathyAccountSettings *settings,
998   const gchar *name,
999   GAsyncReadyCallback callback,
1000   gpointer user_data)
1001 {
1002   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1003   GSimpleAsyncResult *result;
1004
1005   result = g_simple_async_result_new (G_OBJECT (settings),
1006       callback, user_data, empathy_account_settings_set_display_name_finish);
1007
1008   if (!tp_strdiff (name, priv->display_name))
1009     {
1010       /* Nothing to do */
1011       g_simple_async_result_complete_in_idle (result);
1012       return;
1013     }
1014
1015   if (priv->account == NULL)
1016     {
1017       if (priv->display_name != NULL)
1018         g_free (priv->display_name);
1019
1020       priv->display_name = g_strdup (name);
1021
1022       g_simple_async_result_complete_in_idle (result);
1023
1024       return;
1025     }
1026
1027   tp_account_set_display_name_async (priv->account, name,
1028       account_settings_display_name_set_cb, result);
1029 }
1030
1031 gboolean
1032 empathy_account_settings_set_display_name_finish (
1033   EmpathyAccountSettings *settings,
1034   GAsyncResult *result,
1035   GError **error)
1036 {
1037   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1038       error))
1039     return FALSE;
1040
1041   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1042     G_OBJECT (settings), empathy_account_settings_set_display_name_finish),
1043       FALSE);
1044
1045   return TRUE;
1046 }
1047
1048 static void
1049 account_settings_icon_name_set_cb (GObject *src,
1050     GAsyncResult *res,
1051     gpointer user_data)
1052 {
1053   GError *error = NULL;
1054   TpAccount *account = TP_ACCOUNT (src);
1055   GSimpleAsyncResult *set_result = user_data;
1056
1057   tp_account_set_icon_name_finish (account, res, &error);
1058
1059   if (error != NULL)
1060     {
1061       g_simple_async_result_set_from_error (set_result, error);
1062       g_error_free (error);
1063     }
1064
1065   g_simple_async_result_complete (set_result);
1066   g_object_unref (set_result);
1067 }
1068
1069 void
1070 empathy_account_settings_set_icon_name_async (
1071   EmpathyAccountSettings *settings,
1072   const gchar *name,
1073   GAsyncReadyCallback callback,
1074   gpointer user_data)
1075 {
1076   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1077   GSimpleAsyncResult *result;
1078
1079   result = g_simple_async_result_new (G_OBJECT (settings),
1080       callback, user_data, empathy_account_settings_set_icon_name_finish);
1081
1082   if (priv->account == NULL)
1083     {
1084       if (priv->icon_name != NULL)
1085         g_free (priv->icon_name);
1086
1087       priv->icon_name = g_strdup (name);
1088
1089       g_simple_async_result_complete_in_idle (result);
1090
1091       return;
1092     }
1093
1094   tp_account_set_icon_name_async (priv->account, name,
1095       account_settings_icon_name_set_cb, result);
1096 }
1097
1098 gboolean
1099 empathy_account_settings_set_icon_name_finish (
1100   EmpathyAccountSettings *settings,
1101   GAsyncResult *result,
1102   GError **error)
1103 {
1104   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1105       error))
1106     return FALSE;
1107
1108   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1109     G_OBJECT (settings), empathy_account_settings_set_icon_name_finish),
1110       FALSE);
1111
1112   return TRUE;
1113 }
1114
1115 static void
1116 empathy_account_settings_account_updated (GObject *source,
1117     GAsyncResult *result,
1118     gpointer user_data)
1119 {
1120   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1121   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1122   GSimpleAsyncResult *r;
1123   GError *error = NULL;
1124
1125   if (!tp_account_update_parameters_finish (TP_ACCOUNT (source),
1126           result, NULL, &error))
1127     {
1128       g_simple_async_result_set_from_error (priv->apply_result, error);
1129       g_error_free (error);
1130     }
1131   else
1132     {
1133       empathy_account_settings_discard_changes (settings);
1134     }
1135
1136   r = priv->apply_result;
1137   priv->apply_result = NULL;
1138
1139   g_simple_async_result_complete (r);
1140   g_object_unref (r);
1141 }
1142
1143 static void
1144 empathy_account_settings_created_cb (GObject *source,
1145     GAsyncResult *result,
1146     gpointer user_data)
1147 {
1148   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1149   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1150   TpAccount *account;
1151   GError *error = NULL;
1152   GSimpleAsyncResult *r;
1153
1154   account = tp_account_manager_create_account_finish (
1155     TP_ACCOUNT_MANAGER (source), result, &error);
1156
1157   if (account == NULL)
1158     {
1159       g_simple_async_result_set_from_error (priv->apply_result, error);
1160     }
1161   else
1162     {
1163       priv->account = g_object_ref (account);
1164       empathy_account_settings_discard_changes (settings);
1165     }
1166
1167   r = priv->apply_result;
1168   priv->apply_result = NULL;
1169
1170   g_simple_async_result_complete (r);
1171   g_object_unref (r);
1172 }
1173
1174
1175 static void
1176 empathy_account_settings_do_create_account (EmpathyAccountSettings *settings)
1177 {
1178   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1179   GHashTable *properties;
1180   TpConnectionPresenceType type;
1181   gchar *status;
1182   gchar *message;
1183   EmpathyIdle *idle;
1184
1185   properties = tp_asv_new (NULL, NULL);
1186
1187   idle = empathy_idle_dup_singleton ();
1188   type = empathy_idle_get_requested_presence (idle, &status, &message);
1189   g_object_unref (idle);
1190
1191   if (type != TP_CONNECTION_PRESENCE_TYPE_UNSET)
1192     {
1193       /* Create the account with the requested presence the same as the current
1194         * global requested presence, but don't enable it */
1195       GValueArray *presence;
1196       GValue vtype = { 0, };
1197       GValue vstatus = { 0, };
1198       GValue vmessage = { 0, };
1199
1200       presence = g_value_array_new (3);
1201
1202       g_value_init (&vtype, G_TYPE_UINT);
1203       g_value_set_uint (&vtype, type);
1204       g_value_array_append (presence, &vtype);
1205
1206       g_value_init (&vstatus, G_TYPE_STRING);
1207       g_value_take_string (&vstatus, status);
1208       g_value_array_append (presence, &vstatus);
1209
1210       g_value_init (&vmessage, G_TYPE_STRING);
1211       g_value_take_string (&vmessage, message);
1212       g_value_array_append (presence, &vmessage);
1213
1214       tp_asv_take_boxed (properties, TP_IFACE_ACCOUNT ".RequestedPresence",
1215         TP_STRUCT_TYPE_SIMPLE_PRESENCE, presence);
1216     }
1217
1218   tp_asv_set_string (properties, TP_IFACE_ACCOUNT ".Icon",
1219       priv->icon_name);
1220
1221   if (priv->service != NULL)
1222     tp_asv_set_string (properties, TP_PROP_ACCOUNT_SERVICE, priv->service);
1223
1224   tp_account_manager_create_account_async (priv->account_manager,
1225     priv->cm_name, priv->protocol, priv->display_name,
1226     priv->parameters, properties,
1227     empathy_account_settings_created_cb,
1228     settings);
1229
1230   g_hash_table_unref (properties);
1231 }
1232
1233 static void
1234 empathy_account_settings_manager_ready_cb (GObject *source_object,
1235     GAsyncResult *result,
1236     gpointer user_data)
1237 {
1238   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1239   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1240   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1241   GError *error = NULL;
1242
1243   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1244     {
1245       DEBUG ("Failed to prepare account manager: %s", error->message);
1246       g_error_free (error);
1247       return;
1248     }
1249
1250   g_assert (priv->apply_result != NULL && priv->account == NULL);
1251   empathy_account_settings_do_create_account (settings);
1252 }
1253
1254 void
1255 empathy_account_settings_apply_async (EmpathyAccountSettings *settings,
1256     GAsyncReadyCallback callback,
1257     gpointer user_data)
1258 {
1259   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1260
1261   if (priv->apply_result != NULL)
1262     {
1263       g_simple_async_report_error_in_idle (G_OBJECT (settings),
1264           callback, user_data,
1265           G_IO_ERROR, G_IO_ERROR_PENDING, "Applying already in progress");
1266       return;
1267     }
1268
1269   priv->apply_result = g_simple_async_result_new (G_OBJECT (settings),
1270       callback, user_data, empathy_account_settings_apply_finish);
1271
1272   if (priv->account == NULL)
1273     {
1274       tp_account_manager_prepare_async (priv->account_manager, NULL,
1275           empathy_account_settings_manager_ready_cb, settings);
1276     }
1277   else
1278     {
1279       tp_account_update_parameters_async (priv->account,
1280           priv->parameters, (const gchar **)priv->unset_parameters->data,
1281           empathy_account_settings_account_updated, settings);
1282     }
1283 }
1284
1285 gboolean
1286 empathy_account_settings_apply_finish (EmpathyAccountSettings *settings,
1287     GAsyncResult *result,
1288     GError **error)
1289 {
1290   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1291       error))
1292     return FALSE;
1293
1294   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1295     G_OBJECT (settings), empathy_account_settings_apply_finish), FALSE);
1296
1297   return TRUE;
1298 }
1299
1300 gboolean
1301 empathy_account_settings_has_account (EmpathyAccountSettings *settings,
1302     TpAccount *account)
1303 {
1304   EmpathyAccountSettingsPriv *priv;
1305   const gchar *account_path;
1306   const gchar *priv_account_path;
1307
1308   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1309   g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
1310
1311   priv = GET_PRIV (settings);
1312
1313   if (priv->account == NULL)
1314     return FALSE;
1315
1316   account_path = tp_proxy_get_object_path (TP_PROXY (account));
1317   priv_account_path = tp_proxy_get_object_path (TP_PROXY (priv->account));
1318
1319   return (!tp_strdiff (account_path, priv_account_path));
1320 }
1321
1322 gboolean
1323 empathy_account_settings_is_valid (EmpathyAccountSettings *settings)
1324 {
1325   EmpathyAccountSettingsPriv *priv;
1326   guint idx;
1327   gchar *current;
1328   gboolean missed = FALSE;
1329
1330   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1331
1332   priv = GET_PRIV (settings);
1333
1334   for (idx = 0; idx < priv->required_params->len; idx++)
1335     {
1336       current = g_array_index (priv->required_params, gchar *, idx);
1337
1338       /* first, look if it's set in our own parameters */
1339       if (tp_asv_lookup (priv->parameters, current))
1340         continue;
1341
1342       /* if we did not unset the parameter, look if it's in the account */
1343       if (priv->account != NULL &&
1344           !empathy_account_settings_is_unset (settings, current))
1345         {
1346           const GHashTable *account_params;
1347
1348           account_params = tp_account_get_parameters (priv->account);
1349           if (tp_asv_lookup (account_params, current))
1350             continue;
1351         }
1352
1353       missed = TRUE;
1354       break;
1355     }
1356
1357   return !missed;
1358 }
1359
1360 const TpConnectionManagerProtocol *
1361 empathy_account_settings_get_tp_protocol (EmpathyAccountSettings *self)
1362 {
1363   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1364
1365   return tp_connection_manager_get_protocol (priv->manager, priv->protocol);
1366 }