]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account-settings.c
voip_cmp_func: first check if we support and then audio
[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       tp_g_signal_connect_object (priv->managers, "notify::ready",
205         G_CALLBACK (empathy_account_settings_managers_ready_cb), object, 0);
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 const gchar * const *
686 empathy_account_settings_get_strv (EmpathyAccountSettings *settings,
687     const gchar *param)
688 {
689   const GValue *v;
690
691   v = empathy_account_settings_get (settings, param);
692
693   if (v == NULL || !G_VALUE_HOLDS (v, G_TYPE_STRV))
694     return NULL;
695
696   return g_value_get_boxed (v);
697 }
698
699 gint32
700 empathy_account_settings_get_int32 (EmpathyAccountSettings *settings,
701     const gchar *param)
702 {
703   const GValue *v;
704   gint32 ret = 0;
705
706   v = empathy_account_settings_get (settings, param);
707
708   if (v == NULL)
709     return 0;
710
711   switch G_VALUE_TYPE (v)
712     {
713       case G_TYPE_UCHAR:
714         ret = g_value_get_uchar (v);
715         break;
716       case G_TYPE_INT:
717         ret = g_value_get_int (v);
718         break;
719       case G_TYPE_UINT:
720         ret = CLAMP (g_value_get_uint (v), (guint) G_MININT32,
721             G_MAXINT32);
722         break;
723       case G_TYPE_INT64:
724         ret = CLAMP (g_value_get_int64 (v), G_MININT32, G_MAXINT32);
725         break;
726       case G_TYPE_UINT64:
727         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT32,
728             G_MAXINT32);
729         break;
730       default:
731         ret = 0;
732         break;
733     }
734
735   return ret;
736 }
737
738 gint64
739 empathy_account_settings_get_int64 (EmpathyAccountSettings *settings,
740     const gchar *param)
741 {
742   const GValue *v;
743   gint64 ret = 0;
744
745   v = empathy_account_settings_get (settings, param);
746   if (v == NULL)
747     return 0;
748
749   switch G_VALUE_TYPE (v)
750     {
751       case G_TYPE_UCHAR:
752         ret = g_value_get_uchar (v);
753         break;
754       case G_TYPE_INT:
755         ret = g_value_get_int (v);
756         break;
757       case G_TYPE_UINT:
758         ret = g_value_get_uint (v);
759         break;
760       case G_TYPE_INT64:
761         ret = g_value_get_int64 (v);
762         break;
763       case G_TYPE_UINT64:
764         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT64, G_MAXINT64);
765         break;
766       default:
767         ret = 0;
768         break;
769     }
770
771   return ret;
772 }
773
774 guint32
775 empathy_account_settings_get_uint32 (EmpathyAccountSettings *settings,
776     const gchar *param)
777 {
778   const GValue *v;
779   guint32 ret;
780
781   v = empathy_account_settings_get (settings, param);
782   if (v == NULL)
783     return 0;
784
785   switch G_VALUE_TYPE (v)
786     {
787       case G_TYPE_UCHAR:
788         ret = g_value_get_uchar (v);
789         break;
790       case G_TYPE_INT:
791         ret = MAX (0, g_value_get_int (v));
792         break;
793       case G_TYPE_UINT:
794         ret = g_value_get_uint (v);
795         break;
796       case G_TYPE_INT64:
797         ret = CLAMP (g_value_get_int64 (v), 0, G_MAXUINT32);
798         break;
799       case G_TYPE_UINT64:
800         ret = MIN (g_value_get_uint64 (v), G_MAXUINT32);
801         break;
802       default:
803         ret = 0;
804         break;
805     }
806
807   return ret;
808 }
809
810 guint64
811 empathy_account_settings_get_uint64 (EmpathyAccountSettings *settings,
812     const gchar *param)
813 {
814   const GValue *v;
815   guint64 ret = 0;
816
817   v = empathy_account_settings_get (settings, param);
818
819   if (v == NULL || !G_VALUE_HOLDS_INT (v))
820     return 0;
821
822   switch G_VALUE_TYPE (v)
823     {
824       case G_TYPE_UCHAR:
825         ret = g_value_get_uchar (v);
826         break;
827       case G_TYPE_INT:
828         ret = MAX (0, g_value_get_int (v));
829         break;
830       case G_TYPE_UINT:
831         ret = g_value_get_uint (v);
832         break;
833       case G_TYPE_INT64:
834         ret = MAX (0, g_value_get_int64 (v));
835         break;
836       case G_TYPE_UINT64:
837         ret = g_value_get_uint64 (v);
838         break;
839       default:
840         ret = 0;
841         break;
842     }
843
844   return ret;
845 }
846
847 gboolean
848 empathy_account_settings_get_boolean (EmpathyAccountSettings *settings,
849     const gchar *param)
850 {
851   const GValue *v;
852
853   v = empathy_account_settings_get (settings, param);
854
855   if (v == NULL || !G_VALUE_HOLDS_BOOLEAN (v))
856     return FALSE;
857
858   return g_value_get_boolean (v);
859 }
860
861 void
862 empathy_account_settings_set_string (EmpathyAccountSettings *settings,
863     const gchar *param,
864     const gchar *value)
865 {
866   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
867
868   tp_asv_set_string (priv->parameters, g_strdup (param), value);
869
870   account_settings_remove_from_unset (settings, param);
871 }
872
873 void
874 empathy_account_settings_set_strv (EmpathyAccountSettings *settings,
875     const gchar *param,
876     gchar **value)
877 {
878   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
879
880   tp_asv_set_strv (priv->parameters, g_strdup (param), value);
881
882   account_settings_remove_from_unset (settings, param);
883 }
884
885 void
886 empathy_account_settings_set_int32 (EmpathyAccountSettings *settings,
887     const gchar *param,
888     gint32 value)
889 {
890   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
891
892   tp_asv_set_int32 (priv->parameters, g_strdup (param), value);
893
894   account_settings_remove_from_unset (settings, param);
895 }
896
897 void
898 empathy_account_settings_set_int64 (EmpathyAccountSettings *settings,
899     const gchar *param,
900     gint64 value)
901 {
902   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
903
904   tp_asv_set_int64 (priv->parameters, g_strdup (param), value);
905
906   account_settings_remove_from_unset (settings, param);
907 }
908
909 void
910 empathy_account_settings_set_uint32 (EmpathyAccountSettings *settings,
911     const gchar *param,
912     guint32 value)
913 {
914   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
915
916   tp_asv_set_uint32 (priv->parameters, g_strdup (param), value);
917
918   account_settings_remove_from_unset (settings, param);
919 }
920
921 void
922 empathy_account_settings_set_uint64 (EmpathyAccountSettings *settings,
923     const gchar *param,
924     guint64 value)
925 {
926   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
927
928   tp_asv_set_uint64 (priv->parameters, g_strdup (param), value);
929
930   account_settings_remove_from_unset (settings, param);
931 }
932
933 void
934 empathy_account_settings_set_boolean (EmpathyAccountSettings *settings,
935     const gchar *param,
936     gboolean value)
937 {
938   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
939
940   tp_asv_set_boolean (priv->parameters, g_strdup (param), value);
941
942   account_settings_remove_from_unset (settings, param);
943 }
944
945 static void
946 account_settings_display_name_set_cb (GObject *src,
947     GAsyncResult *res,
948     gpointer user_data)
949 {
950   GError *error = NULL;
951   TpAccount *account = TP_ACCOUNT (src);
952   GSimpleAsyncResult *set_result = user_data;
953
954   tp_account_set_display_name_finish (account, res, &error);
955
956   if (error != NULL)
957     {
958       g_simple_async_result_set_from_error (set_result, error);
959       g_error_free (error);
960     }
961
962   g_simple_async_result_complete (set_result);
963   g_object_unref (set_result);
964 }
965
966 void
967 empathy_account_settings_set_display_name_async (
968   EmpathyAccountSettings *settings,
969   const gchar *name,
970   GAsyncReadyCallback callback,
971   gpointer user_data)
972 {
973   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
974   GSimpleAsyncResult *result;
975
976   result = g_simple_async_result_new (G_OBJECT (settings),
977       callback, user_data, empathy_account_settings_set_display_name_finish);
978
979   if (!tp_strdiff (name, priv->display_name))
980     {
981       /* Nothing to do */
982       g_simple_async_result_complete_in_idle (result);
983       return;
984     }
985
986   if (priv->account == NULL)
987     {
988       if (priv->display_name != NULL)
989         g_free (priv->display_name);
990
991       priv->display_name = g_strdup (name);
992
993       g_simple_async_result_complete_in_idle (result);
994
995       return;
996     }
997
998   tp_account_set_display_name_async (priv->account, name,
999       account_settings_display_name_set_cb, result);
1000 }
1001
1002 gboolean
1003 empathy_account_settings_set_display_name_finish (
1004   EmpathyAccountSettings *settings,
1005   GAsyncResult *result,
1006   GError **error)
1007 {
1008   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1009       error))
1010     return FALSE;
1011
1012   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1013     G_OBJECT (settings), empathy_account_settings_set_display_name_finish),
1014       FALSE);
1015
1016   return TRUE;
1017 }
1018
1019 static void
1020 account_settings_icon_name_set_cb (GObject *src,
1021     GAsyncResult *res,
1022     gpointer user_data)
1023 {
1024   GError *error = NULL;
1025   TpAccount *account = TP_ACCOUNT (src);
1026   GSimpleAsyncResult *set_result = user_data;
1027
1028   tp_account_set_icon_name_finish (account, res, &error);
1029
1030   if (error != NULL)
1031     {
1032       g_simple_async_result_set_from_error (set_result, error);
1033       g_error_free (error);
1034     }
1035
1036   g_simple_async_result_complete (set_result);
1037   g_object_unref (set_result);
1038 }
1039
1040 void
1041 empathy_account_settings_set_icon_name_async (
1042   EmpathyAccountSettings *settings,
1043   const gchar *name,
1044   GAsyncReadyCallback callback,
1045   gpointer user_data)
1046 {
1047   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1048   GSimpleAsyncResult *result;
1049
1050   result = g_simple_async_result_new (G_OBJECT (settings),
1051       callback, user_data, empathy_account_settings_set_icon_name_finish);
1052
1053   if (priv->account == NULL)
1054     {
1055       if (priv->icon_name != NULL)
1056         g_free (priv->icon_name);
1057
1058       priv->icon_name = g_strdup (name);
1059
1060       g_simple_async_result_complete_in_idle (result);
1061
1062       return;
1063     }
1064
1065   tp_account_set_icon_name_async (priv->account, name,
1066       account_settings_icon_name_set_cb, result);
1067 }
1068
1069 gboolean
1070 empathy_account_settings_set_icon_name_finish (
1071   EmpathyAccountSettings *settings,
1072   GAsyncResult *result,
1073   GError **error)
1074 {
1075   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1076       error))
1077     return FALSE;
1078
1079   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1080     G_OBJECT (settings), empathy_account_settings_set_icon_name_finish),
1081       FALSE);
1082
1083   return TRUE;
1084 }
1085
1086 static void
1087 empathy_account_settings_account_updated (GObject *source,
1088     GAsyncResult *result,
1089     gpointer user_data)
1090 {
1091   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1092   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1093   GSimpleAsyncResult *r;
1094   GError *error = NULL;
1095
1096   if (!tp_account_update_parameters_finish (TP_ACCOUNT (source),
1097           result, NULL, &error))
1098     {
1099       g_simple_async_result_set_from_error (priv->apply_result, error);
1100       g_error_free (error);
1101     }
1102   else
1103     {
1104       empathy_account_settings_discard_changes (settings);
1105     }
1106
1107   r = priv->apply_result;
1108   priv->apply_result = NULL;
1109
1110   g_simple_async_result_complete (r);
1111   g_object_unref (r);
1112 }
1113
1114 static void
1115 empathy_account_settings_created_cb (GObject *source,
1116     GAsyncResult *result,
1117     gpointer user_data)
1118 {
1119   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1120   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1121   TpAccount *account;
1122   GError *error = NULL;
1123   GSimpleAsyncResult *r;
1124
1125   account = tp_account_manager_create_account_finish (
1126     TP_ACCOUNT_MANAGER (source), result, &error);
1127
1128   if (account == NULL)
1129     {
1130       g_simple_async_result_set_from_error (priv->apply_result, error);
1131     }
1132   else
1133     {
1134       priv->account = g_object_ref (account);
1135       empathy_account_settings_discard_changes (settings);
1136     }
1137
1138   r = priv->apply_result;
1139   priv->apply_result = NULL;
1140
1141   g_simple_async_result_complete (r);
1142   g_object_unref (r);
1143 }
1144
1145
1146 static void
1147 empathy_account_settings_do_create_account (EmpathyAccountSettings *settings)
1148 {
1149   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1150   GHashTable *properties;
1151   TpConnectionPresenceType type;
1152   gchar *status;
1153   gchar *message;
1154   EmpathyIdle *idle;
1155
1156   properties = tp_asv_new (NULL, NULL);
1157
1158   idle = empathy_idle_dup_singleton ();
1159   type = empathy_idle_get_requested_presence (idle, &status, &message);
1160   g_object_unref (idle);
1161
1162   if (type != TP_CONNECTION_PRESENCE_TYPE_UNSET)
1163     {
1164       /* Create the account with the requested presence the same as the current
1165         * global requested presence, but don't enable it */
1166       GValueArray *presence;
1167       GValue vtype = { 0, };
1168       GValue vstatus = { 0, };
1169       GValue vmessage = { 0, };
1170
1171       presence = g_value_array_new (3);
1172
1173       g_value_init (&vtype, G_TYPE_UINT);
1174       g_value_set_uint (&vtype, type);
1175       g_value_array_append (presence, &vtype);
1176
1177       g_value_init (&vstatus, G_TYPE_STRING);
1178       g_value_take_string (&vstatus, status);
1179       g_value_array_append (presence, &vstatus);
1180
1181       g_value_init (&vmessage, G_TYPE_STRING);
1182       g_value_take_string (&vmessage, message);
1183       g_value_array_append (presence, &vmessage);
1184
1185       tp_asv_take_boxed (properties, TP_IFACE_ACCOUNT ".RequestedPresence",
1186         TP_STRUCT_TYPE_SIMPLE_PRESENCE, presence);
1187     }
1188
1189   tp_asv_set_string (properties, TP_IFACE_ACCOUNT ".Icon",
1190       priv->icon_name);
1191
1192   tp_account_manager_create_account_async (priv->account_manager,
1193     priv->cm_name, priv->protocol, priv->display_name,
1194     priv->parameters, properties,
1195     empathy_account_settings_created_cb,
1196     settings);
1197
1198   g_hash_table_unref (properties);
1199 }
1200
1201 static void
1202 empathy_account_settings_manager_ready_cb (GObject *source_object,
1203     GAsyncResult *result,
1204     gpointer user_data)
1205 {
1206   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1207   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1208   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1209   GError *error = NULL;
1210
1211   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1212     {
1213       DEBUG ("Failed to prepare account manager: %s", error->message);
1214       g_error_free (error);
1215       return;
1216     }
1217
1218   g_assert (priv->apply_result != NULL && priv->account == NULL);
1219   empathy_account_settings_do_create_account (settings);
1220 }
1221
1222 void
1223 empathy_account_settings_apply_async (EmpathyAccountSettings *settings,
1224     GAsyncReadyCallback callback,
1225     gpointer user_data)
1226 {
1227   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1228
1229   if (priv->apply_result != NULL)
1230     {
1231       g_simple_async_report_error_in_idle (G_OBJECT (settings),
1232           callback, user_data,
1233           G_IO_ERROR, G_IO_ERROR_PENDING, "Applying already in progress");
1234       return;
1235     }
1236
1237   priv->apply_result = g_simple_async_result_new (G_OBJECT (settings),
1238       callback, user_data, empathy_account_settings_apply_finish);
1239
1240   if (priv->account == NULL)
1241     {
1242       tp_account_manager_prepare_async (priv->account_manager, NULL,
1243           empathy_account_settings_manager_ready_cb, settings);
1244     }
1245   else
1246     {
1247       tp_account_update_parameters_async (priv->account,
1248           priv->parameters, (const gchar **)priv->unset_parameters->data,
1249           empathy_account_settings_account_updated, settings);
1250     }
1251 }
1252
1253 gboolean
1254 empathy_account_settings_apply_finish (EmpathyAccountSettings *settings,
1255     GAsyncResult *result,
1256     GError **error)
1257 {
1258   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1259       error))
1260     return FALSE;
1261
1262   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1263     G_OBJECT (settings), empathy_account_settings_apply_finish), FALSE);
1264
1265   return TRUE;
1266 }
1267
1268 gboolean
1269 empathy_account_settings_has_account (EmpathyAccountSettings *settings,
1270     TpAccount *account)
1271 {
1272   EmpathyAccountSettingsPriv *priv;
1273   const gchar *account_path;
1274   const gchar *priv_account_path;
1275
1276   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1277   g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
1278
1279   priv = GET_PRIV (settings);
1280
1281   if (priv->account == NULL)
1282     return FALSE;
1283
1284   account_path = tp_proxy_get_object_path (TP_PROXY (account));
1285   priv_account_path = tp_proxy_get_object_path (TP_PROXY (priv->account));
1286
1287   return (!tp_strdiff (account_path, priv_account_path));
1288 }
1289
1290 gboolean
1291 empathy_account_settings_is_valid (EmpathyAccountSettings *settings)
1292 {
1293   EmpathyAccountSettingsPriv *priv;
1294   guint idx;
1295   gchar *current;
1296   gboolean missed = FALSE;
1297
1298   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1299
1300   priv = GET_PRIV (settings);
1301
1302   for (idx = 0; idx < priv->required_params->len; idx++)
1303     {
1304       current = g_array_index (priv->required_params, gchar *, idx);
1305
1306       /* first, look if it's set in our own parameters */
1307       if (tp_asv_lookup (priv->parameters, current))
1308         continue;
1309
1310       /* if we did not unset the parameter, look if it's in the account */
1311       if (priv->account != NULL &&
1312           !empathy_account_settings_is_unset (settings, current))
1313         {
1314           const GHashTable *account_params;
1315
1316           account_params = tp_account_get_parameters (priv->account);
1317           if (tp_asv_lookup (account_params, current))
1318             continue;
1319         }
1320
1321       missed = TRUE;
1322       break;
1323     }
1324
1325   return !missed;
1326 }
1327
1328 const TpConnectionManagerProtocol *
1329 empathy_account_settings_get_tp_protocol (EmpathyAccountSettings *self)
1330 {
1331   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1332
1333   return tp_connection_manager_get_protocol (priv->manager, priv->protocol);
1334 }