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