]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account-settings.c
account-settings: store the TpProtocol object and notify::ready when it's prepared
[empathy.git] / libempathy / empathy-account-settings.c
1 /*
2  * empathy-account-settings.c - Source for EmpathyAccountSettings
3  * Copyright (C) 2009 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/account-manager.h>
26 #include <telepathy-glib/util.h>
27 #include <telepathy-glib/interfaces.h>
28 #include <telepathy-glib/gtypes.h>
29
30 #include "empathy-account-settings.h"
31 #include "empathy-connection-managers.h"
32 #include "empathy-utils.h"
33 #include "empathy-idle.h"
34
35 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
36 #include <libempathy/empathy-debug.h>
37
38 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountSettings)
39
40 G_DEFINE_TYPE(EmpathyAccountSettings, empathy_account_settings, G_TYPE_OBJECT)
41
42 enum {
43   PROP_ACCOUNT = 1,
44   PROP_CM_NAME,
45   PROP_PROTOCOL,
46   PROP_SERVICE,
47   PROP_DISPLAY_NAME,
48   PROP_DISPLAY_NAME_OVERRIDDEN,
49   PROP_READY
50 };
51
52 /* private structure */
53 typedef struct _EmpathyAccountSettingsPriv EmpathyAccountSettingsPriv;
54
55 struct _EmpathyAccountSettingsPriv
56 {
57   gboolean dispose_has_run;
58   EmpathyConnectionManagers *managers;
59   TpAccountManager *account_manager;
60
61   TpConnectionManager *manager;
62   TpProtocol *protocol_obj;
63
64   TpAccount *account;
65   gchar *cm_name;
66   gchar *protocol;
67   gchar *service;
68   gchar *display_name;
69   gchar *icon_name;
70   gboolean display_name_overridden;
71   gboolean ready;
72
73   /* Parameter name (gchar *) -> parameter value (GValue) */
74   GHashTable *parameters;
75   /* Keys are parameter names from the hash above (gchar *).
76    * Values are regular expresions that should match corresponding parameter
77    * values (GRegex *). Possible regexp patterns are defined in
78    * empathy-account-widget.c */
79   GHashTable *param_regexps;
80   GArray *unset_parameters;
81   GList *required_params;
82
83   gulong managers_ready_id;
84   gboolean preparing_protocol;
85
86   GSimpleAsyncResult *apply_result;
87 };
88
89 static void
90 empathy_account_settings_init (EmpathyAccountSettings *obj)
91 {
92   EmpathyAccountSettingsPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE ((obj),
93     EMPATHY_TYPE_ACCOUNT_SETTINGS, EmpathyAccountSettingsPriv);
94
95   obj->priv = priv;
96
97   /* allocate any data required by the object here */
98   priv->managers = empathy_connection_managers_dup_singleton ();
99   priv->account_manager = tp_account_manager_dup ();
100
101   priv->parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
102     g_free, (GDestroyNotify) tp_g_value_slice_free);
103
104   priv->param_regexps = g_hash_table_new_full (g_str_hash, g_str_equal,
105     g_free, (GDestroyNotify) g_regex_unref);
106
107   priv->unset_parameters = g_array_new (TRUE, FALSE, sizeof (gchar *));
108
109   priv->required_params = NULL;
110 }
111
112 static void empathy_account_settings_dispose (GObject *object);
113 static void empathy_account_settings_finalize (GObject *object);
114 static void empathy_account_settings_account_ready_cb (GObject *source_object,
115     GAsyncResult *result, gpointer user_data);
116 static void empathy_account_settings_managers_ready_cb (GObject *obj,
117     GParamSpec *pspec, gpointer user_data);
118 static void empathy_account_settings_check_readyness (
119     EmpathyAccountSettings *self);
120
121 static void
122 empathy_account_settings_set_property (GObject *object,
123     guint prop_id,
124     const GValue *value,
125     GParamSpec *pspec)
126 {
127   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (object);
128   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
129
130   switch (prop_id)
131     {
132       case PROP_ACCOUNT:
133         priv->account = g_value_dup_object (value);
134         break;
135       case PROP_CM_NAME:
136         priv->cm_name = g_value_dup_string (value);
137         break;
138       case PROP_PROTOCOL:
139         priv->protocol = g_value_dup_string (value);
140         break;
141       case PROP_SERVICE:
142         priv->service = g_value_dup_string (value);
143         break;
144       case PROP_DISPLAY_NAME:
145         priv->display_name = g_value_dup_string (value);
146         break;
147       case PROP_DISPLAY_NAME_OVERRIDDEN:
148         priv->display_name_overridden = g_value_get_boolean (value);
149         break;
150       default:
151         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152         break;
153     }
154 }
155
156 static void
157 empathy_account_settings_get_property (GObject *object,
158     guint prop_id,
159     GValue *value,
160     GParamSpec *pspec)
161 {
162   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (object);
163   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
164
165   switch (prop_id)
166     {
167       case PROP_ACCOUNT:
168         g_value_set_object (value, priv->account);
169         break;
170       case PROP_CM_NAME:
171         g_value_set_string (value, priv->cm_name);
172         break;
173       case PROP_PROTOCOL:
174         g_value_set_string (value, priv->protocol);
175         break;
176       case PROP_SERVICE:
177         g_value_set_string (value, priv->service);
178         break;
179       case PROP_DISPLAY_NAME:
180         g_value_set_string (value, priv->display_name);
181         break;
182       case PROP_DISPLAY_NAME_OVERRIDDEN:
183         g_value_set_boolean (value, priv->display_name_overridden);
184         break;
185       case PROP_READY:
186         g_value_set_boolean (value, priv->ready);
187         break;
188       default:
189         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
190         break;
191     }
192 }
193
194 static void
195 empathy_account_settings_constructed (GObject *object)
196 {
197   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
198   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
199
200   if (priv->account != NULL)
201     {
202       g_free (priv->cm_name);
203       g_free (priv->protocol);
204       g_free (priv->service);
205
206       priv->cm_name =
207         g_strdup (tp_account_get_connection_manager (priv->account));
208       priv->protocol =
209         g_strdup (tp_account_get_protocol (priv->account));
210       priv->service =
211         g_strdup (tp_account_get_service (priv->account));
212       priv->icon_name = g_strdup
213         (tp_account_get_icon_name (priv->account));
214     }
215   else
216     {
217       priv->icon_name = empathy_protocol_icon_name (priv->protocol);
218     }
219
220   g_assert (priv->cm_name != NULL && priv->protocol != NULL);
221
222   empathy_account_settings_check_readyness (self);
223
224   if (!priv->ready)
225     {
226       GQuark features[] = {
227           TP_ACCOUNT_FEATURE_CORE,
228           TP_ACCOUNT_FEATURE_STORAGE,
229           0 };
230
231       tp_proxy_prepare_async (priv->account, features,
232           empathy_account_settings_account_ready_cb, self);
233       tp_g_signal_connect_object (priv->managers, "notify::ready",
234         G_CALLBACK (empathy_account_settings_managers_ready_cb), object, 0);
235     }
236
237   if (G_OBJECT_CLASS (
238         empathy_account_settings_parent_class)->constructed != NULL)
239     G_OBJECT_CLASS (
240         empathy_account_settings_parent_class)->constructed (object);
241 }
242
243
244 static void
245 empathy_account_settings_class_init (
246     EmpathyAccountSettingsClass *empathy_account_settings_class)
247 {
248   GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_settings_class);
249
250   g_type_class_add_private (empathy_account_settings_class, sizeof
251       (EmpathyAccountSettingsPriv));
252
253   object_class->dispose = empathy_account_settings_dispose;
254   object_class->finalize = empathy_account_settings_finalize;
255   object_class->set_property = empathy_account_settings_set_property;
256   object_class->get_property = empathy_account_settings_get_property;
257   object_class->constructed = empathy_account_settings_constructed;
258
259   g_object_class_install_property (object_class, PROP_ACCOUNT,
260     g_param_spec_object ("account",
261       "Account",
262       "The TpAccount backing these settings",
263       TP_TYPE_ACCOUNT,
264       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
265
266   g_object_class_install_property (object_class, PROP_CM_NAME,
267     g_param_spec_string ("connection-manager",
268       "connection-manager",
269       "The name of the connection manager this account uses",
270       NULL,
271       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
272
273   g_object_class_install_property (object_class, PROP_PROTOCOL,
274     g_param_spec_string ("protocol",
275       "Protocol",
276       "The name of the protocol this account uses",
277       NULL,
278       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
279
280   g_object_class_install_property (object_class, PROP_SERVICE,
281     g_param_spec_string ("service",
282       "Service",
283       "The service of this account, or NULL",
284       NULL,
285       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
286
287   g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
288     g_param_spec_string ("display-name",
289       "display-name",
290       "The display name account these settings belong to",
291       NULL,
292       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
293
294   g_object_class_install_property (object_class, PROP_DISPLAY_NAME_OVERRIDDEN,
295       g_param_spec_boolean ("display-name-overridden",
296         "display-name-overridden",
297         "Whether the display name for this account has been manually "
298         "overridden",
299         FALSE,
300         G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
301
302   g_object_class_install_property (object_class, PROP_READY,
303     g_param_spec_boolean ("ready",
304       "Ready",
305       "Whether this account is ready to be used",
306       FALSE,
307       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
308 }
309
310 static void
311 empathy_account_settings_dispose (GObject *object)
312 {
313   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
314   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
315
316   if (priv->dispose_has_run)
317     return;
318
319   priv->dispose_has_run = TRUE;
320
321   if (priv->managers_ready_id != 0)
322     g_signal_handler_disconnect (priv->managers, priv->managers_ready_id);
323   priv->managers_ready_id = 0;
324
325   if (priv->managers != NULL)
326     g_object_unref (priv->managers);
327   priv->managers = NULL;
328
329   if (priv->manager != NULL)
330     g_object_unref (priv->manager);
331   priv->manager = NULL;
332
333   if (priv->account_manager != NULL)
334     g_object_unref (priv->account_manager);
335   priv->account_manager = NULL;
336
337   if (priv->account != NULL)
338     g_object_unref (priv->account);
339   priv->account = NULL;
340
341   if (priv->protocol_obj != NULL)
342     g_object_unref (priv->protocol_obj);
343   priv->protocol_obj = NULL;
344
345   /* release any references held by the object here */
346   if (G_OBJECT_CLASS (empathy_account_settings_parent_class)->dispose)
347     G_OBJECT_CLASS (empathy_account_settings_parent_class)->dispose (object);
348 }
349
350 static void
351 empathy_account_settings_free_unset_parameters (
352     EmpathyAccountSettings *settings)
353 {
354   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
355   guint i;
356
357   for (i = 0 ; i < priv->unset_parameters->len; i++)
358     g_free (g_array_index (priv->unset_parameters, gchar *, i));
359
360   g_array_set_size (priv->unset_parameters, 0);
361 }
362
363 static void
364 empathy_account_settings_finalize (GObject *object)
365 {
366   EmpathyAccountSettings *self = EMPATHY_ACCOUNT_SETTINGS (object);
367   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
368   GList *l;
369
370   /* free any data held directly by the object here */
371   g_free (priv->cm_name);
372   g_free (priv->protocol);
373   g_free (priv->service);
374   g_free (priv->display_name);
375   g_free (priv->icon_name);
376
377   if (priv->required_params != NULL)
378     {
379       for (l = priv->required_params; l; l = l->next)
380         g_free (l->data);
381       g_list_free (priv->required_params);
382     }
383
384   g_hash_table_destroy (priv->parameters);
385   g_hash_table_destroy (priv->param_regexps);
386
387   empathy_account_settings_free_unset_parameters (self);
388   g_array_free (priv->unset_parameters, TRUE);
389
390   G_OBJECT_CLASS (empathy_account_settings_parent_class)->finalize (object);
391 }
392
393 static void
394 empathy_account_settings_protocol_obj_prepared_cb (GObject *source,
395     GAsyncResult *result,
396     gpointer user_data)
397 {
398   EmpathyAccountSettings *self = user_data;
399   GError *error = NULL;
400
401   if (!tp_proxy_prepare_finish (source, result, &error))
402     {
403       DEBUG ("Failed to prepare protocol object: %s", error->message);
404       g_clear_error (&error);
405       return;
406     }
407
408   empathy_account_settings_check_readyness (self);
409 }
410
411 static void
412 empathy_account_settings_check_readyness (EmpathyAccountSettings *self)
413 {
414   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
415   const TpConnectionManagerProtocol *tp_protocol;
416   GQuark features[] = { TP_PROTOCOL_FEATURE_CORE, 0 };
417
418   if (priv->ready)
419     return;
420
421   if (priv->account != NULL
422       && !tp_account_is_prepared (priv->account, TP_ACCOUNT_FEATURE_CORE))
423       return;
424
425   if (!empathy_connection_managers_is_ready (priv->managers))
426     return;
427
428   if (priv->manager == NULL)
429     {
430       priv->manager = empathy_connection_managers_get_cm (
431           priv->managers, priv->cm_name);
432     }
433
434   if (priv->manager == NULL)
435     return;
436
437   g_object_ref (priv->manager);
438
439   if (priv->account != NULL)
440     {
441       g_free (priv->display_name);
442       priv->display_name =
443         g_strdup (tp_account_get_display_name (priv->account));
444
445       g_free (priv->icon_name);
446       priv->icon_name =
447         g_strdup (tp_account_get_icon_name (priv->account));
448     }
449
450   tp_protocol = tp_connection_manager_get_protocol (priv->manager,
451     priv->protocol);
452
453   if (tp_protocol == NULL)
454     {
455       priv->manager = NULL;
456       return;
457     }
458
459   if (priv->required_params == NULL)
460     {
461       TpConnectionManagerParam *cur;
462
463       for (cur = tp_protocol->params; cur->name != NULL; cur++)
464         {
465           if (tp_connection_manager_param_is_required (cur))
466             {
467               priv->required_params = g_list_append (priv->required_params,
468                                                      g_strdup (cur->name));
469             }
470         }
471     }
472
473   if (priv->protocol_obj == NULL)
474     {
475       priv->protocol_obj = g_object_ref (
476           tp_connection_manager_get_protocol_object (priv->manager,
477               priv->protocol));
478     }
479
480   if (!tp_proxy_is_prepared (priv->protocol_obj, TP_PROTOCOL_FEATURE_CORE)
481       && !priv->preparing_protocol)
482     {
483       priv->preparing_protocol = TRUE;
484       tp_proxy_prepare_async (priv->protocol_obj, features,
485           empathy_account_settings_protocol_obj_prepared_cb, self);
486       return;
487     }
488
489   priv->ready = TRUE;
490   g_object_notify (G_OBJECT (self), "ready");
491 }
492
493 static void
494 empathy_account_settings_account_ready_cb (GObject *source_object,
495     GAsyncResult *result,
496     gpointer user_data)
497 {
498   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
499   TpAccount *account = TP_ACCOUNT (source_object);
500   GError *error = NULL;
501
502   if (!tp_proxy_prepare_finish (account, result, &error))
503     {
504       DEBUG ("Failed to prepare account: %s", error->message);
505       g_error_free (error);
506       return;
507     }
508
509   empathy_account_settings_check_readyness (settings);
510 }
511
512 static void
513 empathy_account_settings_managers_ready_cb (GObject *object,
514     GParamSpec *pspec,
515     gpointer user_data)
516 {
517   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
518
519   empathy_account_settings_check_readyness (settings);
520 }
521
522 EmpathyAccountSettings *
523 empathy_account_settings_new (const gchar *connection_manager,
524     const gchar *protocol,
525     const gchar *service,
526     const char *display_name)
527 {
528   return g_object_new (EMPATHY_TYPE_ACCOUNT_SETTINGS,
529       "connection-manager", connection_manager,
530       "protocol", protocol,
531       "service", service,
532       "display-name", display_name,
533       NULL);
534 }
535
536 EmpathyAccountSettings *
537 empathy_account_settings_new_for_account (TpAccount *account)
538 {
539   return g_object_new (EMPATHY_TYPE_ACCOUNT_SETTINGS,
540       "account", account,
541       NULL);
542 }
543
544 TpConnectionManagerParam *
545 empathy_account_settings_get_tp_params (EmpathyAccountSettings *settings)
546 {
547   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
548   const TpConnectionManagerProtocol *tp_protocol;
549
550   g_return_val_if_fail (priv->manager != NULL, NULL);
551   g_return_val_if_fail (priv->protocol != NULL, NULL);
552
553   tp_protocol = tp_connection_manager_get_protocol (priv->manager,
554      priv->protocol);
555   if (tp_protocol == NULL)
556     {
557       DEBUG ("Can't retrieve TpConnectionManagerProtocol for protocol '%s'",
558           priv->protocol);
559       return NULL;
560     }
561
562   return tp_protocol->params;
563 }
564
565 gboolean
566 empathy_account_settings_is_ready (EmpathyAccountSettings *settings)
567 {
568   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
569
570   return priv->ready;
571 }
572
573 const gchar *
574 empathy_account_settings_get_cm (EmpathyAccountSettings *settings)
575 {
576   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
577
578   return priv->cm_name;
579 }
580
581 const gchar *
582 empathy_account_settings_get_protocol (EmpathyAccountSettings *settings)
583 {
584   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
585
586   return priv->protocol;
587 }
588
589 const gchar *
590 empathy_account_settings_get_service (EmpathyAccountSettings *settings)
591 {
592   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
593
594   return priv->service;
595 }
596
597 gchar *
598 empathy_account_settings_get_icon_name (EmpathyAccountSettings *settings)
599 {
600   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
601
602   return priv->icon_name;
603 }
604
605 const gchar *
606 empathy_account_settings_get_display_name (EmpathyAccountSettings *settings)
607 {
608   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
609
610   return priv->display_name;
611 }
612
613 TpAccount *
614 empathy_account_settings_get_account (EmpathyAccountSettings *settings)
615 {
616   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
617
618   return priv->account;
619 }
620
621 static gboolean
622 empathy_account_settings_is_unset (EmpathyAccountSettings *settings,
623     const gchar *param)
624 {
625   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
626   GArray *a;
627   guint i;
628
629   a = priv->unset_parameters;
630
631   for (i = 0; i < a->len; i++)
632     {
633       if (!tp_strdiff (g_array_index (a, gchar *, i), param))
634         return TRUE;
635     }
636
637   return FALSE;
638 }
639
640 static TpConnectionManagerParam *
641 empathy_account_settings_get_tp_param (EmpathyAccountSettings *settings,
642     const gchar *param)
643 {
644   TpConnectionManagerParam *tp_params =
645       empathy_account_settings_get_tp_params (settings);
646   TpConnectionManagerParam *p;
647
648   for (p = tp_params; p != NULL && p->name != NULL; p++)
649     {
650       if (tp_strdiff (p->name, param))
651         continue;
652
653       return p;
654     }
655
656   return NULL;
657 }
658
659 static void
660 account_settings_remove_from_unset (EmpathyAccountSettings *settings,
661     const gchar *param)
662 {
663   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
664   guint idx;
665   gchar *val;
666
667   for (idx = 0; idx < priv->unset_parameters->len; idx++)
668     {
669       val = g_array_index (priv->unset_parameters, gchar *, idx);
670
671       if (!tp_strdiff (val, param))
672         {
673           priv->unset_parameters =
674             g_array_remove_index (priv->unset_parameters, idx);
675           g_free (val);
676
677           break;
678         }
679     }
680 }
681
682 const GValue *
683 empathy_account_settings_get_default (EmpathyAccountSettings *settings,
684     const gchar *param)
685 {
686   TpConnectionManagerParam *p;
687
688   p = empathy_account_settings_get_tp_param (settings, param);
689
690   if (p == NULL || !(p->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT))
691     return NULL;
692
693   return &(p->default_value);
694 }
695
696 const gchar *
697 empathy_account_settings_get_dbus_signature (EmpathyAccountSettings *settings,
698     const gchar *param)
699 {
700   TpConnectionManagerParam *p;
701
702   p = empathy_account_settings_get_tp_param (settings, param);
703
704   if (p == NULL)
705     return NULL;
706
707   return p->dbus_signature;
708 }
709
710 const GValue *
711 empathy_account_settings_get (EmpathyAccountSettings *settings,
712     const gchar *param)
713 {
714   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
715   const GValue *result = NULL;
716
717   /* Lookup the update parameters we set */
718   result = tp_asv_lookup (priv->parameters, param);
719   if (result != NULL)
720     return result;
721
722   /* If the parameters isn't unset use the accounts setting if any */
723   if (priv->account != NULL
724       && !empathy_account_settings_is_unset (settings, param))
725     {
726       const GHashTable *parameters;
727
728       parameters = tp_account_get_parameters (priv->account);
729       result = tp_asv_lookup (parameters, param);
730
731       if (result != NULL)
732         return result;
733     }
734
735   /* fallback to the default */
736   return empathy_account_settings_get_default (settings, param);
737 }
738
739 void
740 empathy_account_settings_unset (EmpathyAccountSettings *settings,
741     const gchar *param)
742 {
743   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
744   gchar *v;
745   if (empathy_account_settings_is_unset (settings, param))
746     return;
747
748   v = g_strdup (param);
749
750   g_array_append_val (priv->unset_parameters, v);
751   g_hash_table_remove (priv->parameters, param);
752 }
753
754 void
755 empathy_account_settings_discard_changes (EmpathyAccountSettings *settings)
756 {
757   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
758
759   g_hash_table_remove_all (priv->parameters);
760   empathy_account_settings_free_unset_parameters (settings);
761 }
762
763 const gchar *
764 empathy_account_settings_get_string (EmpathyAccountSettings *settings,
765     const gchar *param)
766 {
767   const GValue *v;
768
769   v = empathy_account_settings_get (settings, param);
770
771   if (v == NULL || !G_VALUE_HOLDS_STRING (v))
772     return NULL;
773
774   return g_value_get_string (v);
775 }
776
777 const gchar * const *
778 empathy_account_settings_get_strv (EmpathyAccountSettings *settings,
779     const gchar *param)
780 {
781   const GValue *v;
782
783   v = empathy_account_settings_get (settings, param);
784
785   if (v == NULL || !G_VALUE_HOLDS (v, G_TYPE_STRV))
786     return NULL;
787
788   return g_value_get_boxed (v);
789 }
790
791 gint32
792 empathy_account_settings_get_int32 (EmpathyAccountSettings *settings,
793     const gchar *param)
794 {
795   const GValue *v;
796   gint32 ret = 0;
797
798   v = empathy_account_settings_get (settings, param);
799
800   if (v == NULL)
801     return 0;
802
803   switch G_VALUE_TYPE (v)
804     {
805       case G_TYPE_UCHAR:
806         ret = g_value_get_uchar (v);
807         break;
808       case G_TYPE_INT:
809         ret = g_value_get_int (v);
810         break;
811       case G_TYPE_UINT:
812         ret = CLAMP (g_value_get_uint (v), (guint) G_MININT32,
813             G_MAXINT32);
814         break;
815       case G_TYPE_INT64:
816         ret = CLAMP (g_value_get_int64 (v), G_MININT32, G_MAXINT32);
817         break;
818       case G_TYPE_UINT64:
819         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT32,
820             G_MAXINT32);
821         break;
822       default:
823         ret = 0;
824         break;
825     }
826
827   return ret;
828 }
829
830 gint64
831 empathy_account_settings_get_int64 (EmpathyAccountSettings *settings,
832     const gchar *param)
833 {
834   const GValue *v;
835   gint64 ret = 0;
836
837   v = empathy_account_settings_get (settings, param);
838   if (v == NULL)
839     return 0;
840
841   switch G_VALUE_TYPE (v)
842     {
843       case G_TYPE_UCHAR:
844         ret = g_value_get_uchar (v);
845         break;
846       case G_TYPE_INT:
847         ret = g_value_get_int (v);
848         break;
849       case G_TYPE_UINT:
850         ret = g_value_get_uint (v);
851         break;
852       case G_TYPE_INT64:
853         ret = g_value_get_int64 (v);
854         break;
855       case G_TYPE_UINT64:
856         ret = CLAMP (g_value_get_uint64 (v), (guint64) G_MININT64, G_MAXINT64);
857         break;
858       default:
859         ret = 0;
860         break;
861     }
862
863   return ret;
864 }
865
866 guint32
867 empathy_account_settings_get_uint32 (EmpathyAccountSettings *settings,
868     const gchar *param)
869 {
870   const GValue *v;
871   guint32 ret;
872
873   v = empathy_account_settings_get (settings, param);
874   if (v == NULL)
875     return 0;
876
877   switch G_VALUE_TYPE (v)
878     {
879       case G_TYPE_UCHAR:
880         ret = g_value_get_uchar (v);
881         break;
882       case G_TYPE_INT:
883         ret = MAX (0, g_value_get_int (v));
884         break;
885       case G_TYPE_UINT:
886         ret = g_value_get_uint (v);
887         break;
888       case G_TYPE_INT64:
889         ret = CLAMP (g_value_get_int64 (v), 0, G_MAXUINT32);
890         break;
891       case G_TYPE_UINT64:
892         ret = MIN (g_value_get_uint64 (v), G_MAXUINT32);
893         break;
894       default:
895         ret = 0;
896         break;
897     }
898
899   return ret;
900 }
901
902 guint64
903 empathy_account_settings_get_uint64 (EmpathyAccountSettings *settings,
904     const gchar *param)
905 {
906   const GValue *v;
907   guint64 ret = 0;
908
909   v = empathy_account_settings_get (settings, param);
910
911   if (v == NULL || !G_VALUE_HOLDS_INT (v))
912     return 0;
913
914   switch G_VALUE_TYPE (v)
915     {
916       case G_TYPE_UCHAR:
917         ret = g_value_get_uchar (v);
918         break;
919       case G_TYPE_INT:
920         ret = MAX (0, g_value_get_int (v));
921         break;
922       case G_TYPE_UINT:
923         ret = g_value_get_uint (v);
924         break;
925       case G_TYPE_INT64:
926         ret = MAX (0, g_value_get_int64 (v));
927         break;
928       case G_TYPE_UINT64:
929         ret = g_value_get_uint64 (v);
930         break;
931       default:
932         ret = 0;
933         break;
934     }
935
936   return ret;
937 }
938
939 gboolean
940 empathy_account_settings_get_boolean (EmpathyAccountSettings *settings,
941     const gchar *param)
942 {
943   const GValue *v;
944
945   v = empathy_account_settings_get (settings, param);
946
947   if (v == NULL || !G_VALUE_HOLDS_BOOLEAN (v))
948     return FALSE;
949
950   return g_value_get_boolean (v);
951 }
952
953 void
954 empathy_account_settings_set_string (EmpathyAccountSettings *settings,
955     const gchar *param,
956     const gchar *value)
957 {
958   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
959
960   g_return_if_fail (param != NULL);
961   g_return_if_fail (value != NULL);
962
963   tp_asv_set_string (priv->parameters, g_strdup (param), value);
964
965   account_settings_remove_from_unset (settings, param);
966 }
967
968 void
969 empathy_account_settings_set_strv (EmpathyAccountSettings *settings,
970     const gchar *param,
971     gchar **value)
972 {
973   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
974
975   g_return_if_fail (param != NULL);
976   g_return_if_fail (value != NULL);
977
978   tp_asv_set_strv (priv->parameters, g_strdup (param), value);
979
980   account_settings_remove_from_unset (settings, param);
981 }
982
983 void
984 empathy_account_settings_set_int32 (EmpathyAccountSettings *settings,
985     const gchar *param,
986     gint32 value)
987 {
988   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
989
990   g_return_if_fail (param != NULL);
991
992   tp_asv_set_int32 (priv->parameters, g_strdup (param), value);
993
994   account_settings_remove_from_unset (settings, param);
995 }
996
997 void
998 empathy_account_settings_set_int64 (EmpathyAccountSettings *settings,
999     const gchar *param,
1000     gint64 value)
1001 {
1002   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1003
1004   g_return_if_fail (param != NULL);
1005
1006   tp_asv_set_int64 (priv->parameters, g_strdup (param), value);
1007
1008   account_settings_remove_from_unset (settings, param);
1009 }
1010
1011 void
1012 empathy_account_settings_set_uint32 (EmpathyAccountSettings *settings,
1013     const gchar *param,
1014     guint32 value)
1015 {
1016   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1017
1018   g_return_if_fail (param != NULL);
1019
1020   tp_asv_set_uint32 (priv->parameters, g_strdup (param), value);
1021
1022   account_settings_remove_from_unset (settings, param);
1023 }
1024
1025 void
1026 empathy_account_settings_set_uint64 (EmpathyAccountSettings *settings,
1027     const gchar *param,
1028     guint64 value)
1029 {
1030   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1031
1032   g_return_if_fail (param != NULL);
1033
1034   tp_asv_set_uint64 (priv->parameters, g_strdup (param), value);
1035
1036   account_settings_remove_from_unset (settings, param);
1037 }
1038
1039 void
1040 empathy_account_settings_set_boolean (EmpathyAccountSettings *settings,
1041     const gchar *param,
1042     gboolean value)
1043 {
1044   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1045
1046   g_return_if_fail (param != NULL);
1047
1048   tp_asv_set_boolean (priv->parameters, g_strdup (param), value);
1049
1050   account_settings_remove_from_unset (settings, param);
1051 }
1052
1053 static void
1054 account_settings_display_name_set_cb (GObject *src,
1055     GAsyncResult *res,
1056     gpointer user_data)
1057 {
1058   GError *error = NULL;
1059   TpAccount *account = TP_ACCOUNT (src);
1060   GSimpleAsyncResult *set_result = user_data;
1061
1062   tp_account_set_display_name_finish (account, res, &error);
1063
1064   if (error != NULL)
1065     {
1066       g_simple_async_result_set_from_error (set_result, error);
1067       g_error_free (error);
1068     }
1069
1070   g_simple_async_result_complete (set_result);
1071   g_object_unref (set_result);
1072 }
1073
1074 void
1075 empathy_account_settings_set_display_name_async (
1076   EmpathyAccountSettings *settings,
1077   const gchar *name,
1078   GAsyncReadyCallback callback,
1079   gpointer user_data)
1080 {
1081   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1082   GSimpleAsyncResult *result;
1083
1084   g_return_if_fail (name != NULL);
1085
1086   result = g_simple_async_result_new (G_OBJECT (settings),
1087       callback, user_data, empathy_account_settings_set_display_name_finish);
1088
1089   if (!tp_strdiff (name, priv->display_name))
1090     {
1091       /* Nothing to do */
1092       g_simple_async_result_complete_in_idle (result);
1093       return;
1094     }
1095
1096   if (priv->account == NULL)
1097     {
1098       if (priv->display_name != NULL)
1099         g_free (priv->display_name);
1100
1101       priv->display_name = g_strdup (name);
1102
1103       g_simple_async_result_complete_in_idle (result);
1104
1105       return;
1106     }
1107
1108   tp_account_set_display_name_async (priv->account, name,
1109       account_settings_display_name_set_cb, result);
1110 }
1111
1112 gboolean
1113 empathy_account_settings_set_display_name_finish (
1114   EmpathyAccountSettings *settings,
1115   GAsyncResult *result,
1116   GError **error)
1117 {
1118   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1119       error))
1120     return FALSE;
1121
1122   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1123     G_OBJECT (settings), empathy_account_settings_set_display_name_finish),
1124       FALSE);
1125
1126   return TRUE;
1127 }
1128
1129 static void
1130 account_settings_icon_name_set_cb (GObject *src,
1131     GAsyncResult *res,
1132     gpointer user_data)
1133 {
1134   GError *error = NULL;
1135   TpAccount *account = TP_ACCOUNT (src);
1136   GSimpleAsyncResult *set_result = user_data;
1137
1138   tp_account_set_icon_name_finish (account, res, &error);
1139
1140   if (error != NULL)
1141     {
1142       g_simple_async_result_set_from_error (set_result, error);
1143       g_error_free (error);
1144     }
1145
1146   g_simple_async_result_complete (set_result);
1147   g_object_unref (set_result);
1148 }
1149
1150 void
1151 empathy_account_settings_set_icon_name_async (
1152   EmpathyAccountSettings *settings,
1153   const gchar *name,
1154   GAsyncReadyCallback callback,
1155   gpointer user_data)
1156 {
1157   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1158   GSimpleAsyncResult *result;
1159
1160   g_return_if_fail (name != NULL);
1161
1162   result = g_simple_async_result_new (G_OBJECT (settings),
1163       callback, user_data, empathy_account_settings_set_icon_name_finish);
1164
1165   if (priv->account == NULL)
1166     {
1167       if (priv->icon_name != NULL)
1168         g_free (priv->icon_name);
1169
1170       priv->icon_name = g_strdup (name);
1171
1172       g_simple_async_result_complete_in_idle (result);
1173
1174       return;
1175     }
1176
1177   tp_account_set_icon_name_async (priv->account, name,
1178       account_settings_icon_name_set_cb, result);
1179 }
1180
1181 gboolean
1182 empathy_account_settings_set_icon_name_finish (
1183   EmpathyAccountSettings *settings,
1184   GAsyncResult *result,
1185   GError **error)
1186 {
1187   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1188       error))
1189     return FALSE;
1190
1191   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1192     G_OBJECT (settings), empathy_account_settings_set_icon_name_finish),
1193       FALSE);
1194
1195   return TRUE;
1196 }
1197
1198 static void
1199 empathy_account_settings_account_updated (GObject *source,
1200     GAsyncResult *result,
1201     gpointer user_data)
1202 {
1203   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1204   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1205   GSimpleAsyncResult *r;
1206   GError *error = NULL;
1207
1208   if (!tp_account_update_parameters_finish (TP_ACCOUNT (source),
1209           result, NULL, &error))
1210     {
1211       g_simple_async_result_set_from_error (priv->apply_result, error);
1212       g_error_free (error);
1213     }
1214   else
1215     {
1216       empathy_account_settings_discard_changes (settings);
1217     }
1218
1219   r = priv->apply_result;
1220   priv->apply_result = NULL;
1221
1222   g_simple_async_result_complete (r);
1223   g_object_unref (r);
1224 }
1225
1226 static void
1227 empathy_account_settings_created_cb (GObject *source,
1228     GAsyncResult *result,
1229     gpointer user_data)
1230 {
1231   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1232   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1233   TpAccount *account;
1234   GError *error = NULL;
1235   GSimpleAsyncResult *r;
1236
1237   account = tp_account_manager_create_account_finish (
1238     TP_ACCOUNT_MANAGER (source), result, &error);
1239
1240   if (account == NULL)
1241     {
1242       g_simple_async_result_set_from_error (priv->apply_result, error);
1243     }
1244   else
1245     {
1246       priv->account = g_object_ref (account);
1247       empathy_account_settings_discard_changes (settings);
1248     }
1249
1250   r = priv->apply_result;
1251   priv->apply_result = NULL;
1252
1253   g_simple_async_result_complete (r);
1254   g_object_unref (r);
1255 }
1256
1257
1258 static void
1259 empathy_account_settings_do_create_account (EmpathyAccountSettings *settings)
1260 {
1261   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1262   GHashTable *properties;
1263   TpConnectionPresenceType type;
1264   gchar *status;
1265   gchar *message;
1266   EmpathyIdle *idle;
1267
1268   properties = tp_asv_new (NULL, NULL);
1269
1270   idle = empathy_idle_dup_singleton ();
1271   type = empathy_idle_get_requested_presence (idle, &status, &message);
1272   g_object_unref (idle);
1273
1274   if (type != TP_CONNECTION_PRESENCE_TYPE_UNSET)
1275     {
1276       /* Create the account with the requested presence the same as the current
1277         * global requested presence, but don't enable it */
1278       GValueArray *presence;
1279       GValue vtype = { 0, };
1280       GValue vstatus = { 0, };
1281       GValue vmessage = { 0, };
1282
1283       presence = g_value_array_new (3);
1284
1285       g_value_init (&vtype, G_TYPE_UINT);
1286       g_value_set_uint (&vtype, type);
1287       g_value_array_append (presence, &vtype);
1288
1289       g_value_init (&vstatus, G_TYPE_STRING);
1290       g_value_take_string (&vstatus, status);
1291       g_value_array_append (presence, &vstatus);
1292
1293       g_value_init (&vmessage, G_TYPE_STRING);
1294       g_value_take_string (&vmessage, message);
1295       g_value_array_append (presence, &vmessage);
1296
1297       tp_asv_take_boxed (properties, TP_IFACE_ACCOUNT ".RequestedPresence",
1298         TP_STRUCT_TYPE_SIMPLE_PRESENCE, presence);
1299     }
1300
1301   tp_asv_set_string (properties, TP_IFACE_ACCOUNT ".Icon",
1302       priv->icon_name);
1303
1304   if (priv->service != NULL)
1305     tp_asv_set_string (properties, TP_PROP_ACCOUNT_SERVICE, priv->service);
1306
1307   tp_account_manager_create_account_async (priv->account_manager,
1308     priv->cm_name, priv->protocol, priv->display_name,
1309     priv->parameters, properties,
1310     empathy_account_settings_created_cb,
1311     settings);
1312
1313   g_hash_table_unref (properties);
1314 }
1315
1316 static void
1317 empathy_account_settings_manager_ready_cb (GObject *source_object,
1318     GAsyncResult *result,
1319     gpointer user_data)
1320 {
1321   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (user_data);
1322   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1323   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1324   GError *error = NULL;
1325
1326   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1327     {
1328       DEBUG ("Failed to prepare account manager: %s", error->message);
1329       g_error_free (error);
1330       return;
1331     }
1332
1333   g_assert (priv->apply_result != NULL && priv->account == NULL);
1334   empathy_account_settings_do_create_account (settings);
1335 }
1336
1337 void
1338 empathy_account_settings_apply_async (EmpathyAccountSettings *settings,
1339     GAsyncReadyCallback callback,
1340     gpointer user_data)
1341 {
1342   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1343
1344   if (priv->apply_result != NULL)
1345     {
1346       g_simple_async_report_error_in_idle (G_OBJECT (settings),
1347           callback, user_data,
1348           G_IO_ERROR, G_IO_ERROR_PENDING, "Applying already in progress");
1349       return;
1350     }
1351
1352   priv->apply_result = g_simple_async_result_new (G_OBJECT (settings),
1353       callback, user_data, empathy_account_settings_apply_finish);
1354
1355   if (priv->account == NULL)
1356     {
1357       tp_account_manager_prepare_async (priv->account_manager, NULL,
1358           empathy_account_settings_manager_ready_cb, settings);
1359     }
1360   else
1361     {
1362       tp_account_update_parameters_async (priv->account,
1363           priv->parameters, (const gchar **)priv->unset_parameters->data,
1364           empathy_account_settings_account_updated, settings);
1365     }
1366 }
1367
1368 gboolean
1369 empathy_account_settings_apply_finish (EmpathyAccountSettings *settings,
1370     GAsyncResult *result,
1371     GError **error)
1372 {
1373   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1374       error))
1375     return FALSE;
1376
1377   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1378     G_OBJECT (settings), empathy_account_settings_apply_finish), FALSE);
1379
1380   return TRUE;
1381 }
1382
1383 gboolean
1384 empathy_account_settings_has_account (EmpathyAccountSettings *settings,
1385     TpAccount *account)
1386 {
1387   EmpathyAccountSettingsPriv *priv;
1388   const gchar *account_path;
1389   const gchar *priv_account_path;
1390
1391   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1392   g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
1393
1394   priv = GET_PRIV (settings);
1395
1396   if (priv->account == NULL)
1397     return FALSE;
1398
1399   account_path = tp_proxy_get_object_path (TP_PROXY (account));
1400   priv_account_path = tp_proxy_get_object_path (TP_PROXY (priv->account));
1401
1402   return (!tp_strdiff (account_path, priv_account_path));
1403 }
1404
1405 void
1406 empathy_account_settings_set_regex (EmpathyAccountSettings *settings,
1407     const gchar *param,
1408     const gchar *pattern)
1409 {
1410   EmpathyAccountSettingsPriv *priv = GET_PRIV (settings);
1411   GRegex *regex;
1412
1413   regex = g_regex_new (pattern, 0, 0, NULL);
1414   g_hash_table_insert (priv->param_regexps, g_strdup (param), regex);
1415 }
1416
1417 gboolean
1418 empathy_account_settings_parameter_is_valid (
1419     EmpathyAccountSettings *settings,
1420     const gchar *param)
1421 {
1422   EmpathyAccountSettingsPriv *priv;
1423   const GRegex *regex;
1424   const gchar *value;
1425
1426   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1427
1428   priv = GET_PRIV (settings);
1429
1430   if (g_list_find_custom (priv->required_params, param, (GCompareFunc) strcmp))
1431     {
1432       /* first, look if it's set in our own parameters */
1433       if (tp_asv_lookup (priv->parameters, param))
1434         goto test_regex;
1435
1436       /* if we did not unset the parameter, look if it's in the account */
1437       if (priv->account != NULL &&
1438           !empathy_account_settings_is_unset (settings, param))
1439         {
1440           const GHashTable *account_params;
1441
1442           account_params = tp_account_get_parameters (priv->account);
1443           if (tp_asv_lookup (account_params, param))
1444             goto test_regex;
1445         }
1446
1447       return FALSE;
1448     }
1449
1450 test_regex:
1451   /* test whether parameter value matches its regex */
1452   regex = g_hash_table_lookup (priv->param_regexps, param);
1453   if (regex)
1454     {
1455       value = empathy_account_settings_get_string (settings, param);
1456       if (value != NULL && !g_regex_match (regex, value, 0, NULL))
1457         return FALSE;
1458     }
1459
1460   return TRUE;
1461 }
1462
1463 gboolean
1464 empathy_account_settings_is_valid (EmpathyAccountSettings *settings)
1465 {
1466   EmpathyAccountSettingsPriv *priv;
1467   const gchar *param;
1468   GHashTableIter iter;
1469   GList *l;
1470
1471   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), FALSE);
1472
1473   priv = GET_PRIV (settings);
1474
1475   for (l = priv->required_params; l; l = l->next)
1476     {
1477       if (!empathy_account_settings_parameter_is_valid (settings, l->data))
1478         return FALSE;
1479     }
1480
1481   g_hash_table_iter_init (&iter, priv->param_regexps);
1482   while (g_hash_table_iter_next (&iter, (gpointer *) &param, NULL))
1483     {
1484       if (!empathy_account_settings_parameter_is_valid (settings, param))
1485         return FALSE;
1486     }
1487
1488   return TRUE;
1489 }
1490
1491 const TpConnectionManagerProtocol *
1492 empathy_account_settings_get_tp_protocol (EmpathyAccountSettings *self)
1493 {
1494   EmpathyAccountSettingsPriv *priv = GET_PRIV (self);
1495
1496   return tp_connection_manager_get_protocol (priv->manager, priv->protocol);
1497 }