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