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