]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account.c
Slightly improve debug info
[empathy.git] / libempathy / empathy-account.c
1 /*
2  * empathy-account.c - Source for EmpathyAccount
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 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <telepathy-glib/enums.h>
26 #include <telepathy-glib/dbus.h>
27 #include <telepathy-glib/account.h>
28 #include <telepathy-glib/gtypes.h>
29 #include <telepathy-glib/util.h>
30 #include <telepathy-glib/interfaces.h>
31 #include <telepathy-glib/defs.h>
32
33 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
34 #include <libempathy/empathy-debug.h>
35
36 #include <glib/gi18n-lib.h>
37
38 #include "empathy-account.h"
39 #include "empathy-account-manager.h"
40 #include "empathy-utils.h"
41 #include "empathy-marshal.h"
42
43 /* signals */
44 enum {
45   STATUS_CHANGED,
46   PRESENCE_CHANGED,
47   REMOVED,
48   LAST_SIGNAL
49 };
50
51 static guint signals[LAST_SIGNAL];
52
53 /* properties */
54 enum {
55   PROP_ENABLED = 1,
56   PROP_PRESENCE,
57   PROP_STATUS,
58   PROP_STATUS_MESSAGE,
59   PROP_READY,
60   PROP_CONNECTION_STATUS,
61   PROP_CONNECTION_STATUS_REASON,
62   PROP_CONNECTION,
63   PROP_UNIQUE_NAME,
64   PROP_DBUS_DAEMON,
65   PROP_DISPLAY_NAME
66 };
67
68 G_DEFINE_TYPE(EmpathyAccount, empathy_account, G_TYPE_OBJECT)
69
70 /* private structure */
71 typedef struct _EmpathyAccountPriv EmpathyAccountPriv;
72
73 struct _EmpathyAccountPriv
74 {
75   gboolean dispose_has_run;
76
77   TpConnection *connection;
78   guint connection_invalidated_id;
79
80   TpConnectionStatus connection_status;
81   TpConnectionStatusReason reason;
82
83   TpConnectionPresenceType presence;
84   gchar *status;
85   gchar *message;
86
87   gboolean enabled;
88   gboolean valid;
89   gboolean ready;
90   gboolean removed;
91   /* Timestamp when the connection got connected in seconds since the epoch */
92   glong connect_time;
93
94   gchar *cm_name;
95   gchar *proto_name;
96   gchar *icon_name;
97
98   gchar *unique_name;
99   gchar *display_name;
100   TpDBusDaemon *dbus;
101
102   TpAccount *account;
103   GHashTable *parameters;
104 };
105
106 #define GET_PRIV(obj)  EMPATHY_GET_PRIV (obj, EmpathyAccount)
107
108 static void _empathy_account_set_connection (EmpathyAccount *account,
109     const gchar *path);
110
111 static void
112 empathy_account_init (EmpathyAccount *obj)
113 {
114   EmpathyAccountPriv *priv;
115
116   priv =  G_TYPE_INSTANCE_GET_PRIVATE (obj,
117     EMPATHY_TYPE_ACCOUNT, EmpathyAccountPriv);
118
119   obj->priv = priv;
120
121   priv->connection_status = TP_CONNECTION_STATUS_DISCONNECTED;
122 }
123
124 static void
125 empathy_account_set_property (GObject *object,
126     guint prop_id,
127     const GValue *value,
128     GParamSpec *pspec)
129 {
130   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
131   EmpathyAccountPriv *priv = GET_PRIV (account);
132
133   switch (prop_id)
134     {
135       case PROP_ENABLED:
136         empathy_account_set_enabled_async (account,
137             g_value_get_boolean (value), NULL, NULL);
138         break;
139       case PROP_UNIQUE_NAME:
140         priv->unique_name = g_value_dup_string (value);
141         break;
142       case PROP_DBUS_DAEMON:
143         priv->dbus = g_value_get_object (value);
144         break;
145       default:
146         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147         break;
148     }
149 }
150
151 static void
152 empathy_account_get_property (GObject *object,
153     guint prop_id,
154     GValue *value,
155     GParamSpec *pspec)
156 {
157   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
158   EmpathyAccountPriv *priv = GET_PRIV (account);
159
160   switch (prop_id)
161     {
162       case PROP_ENABLED:
163         g_value_set_boolean (value, priv->enabled);
164         break;
165       case PROP_READY:
166         g_value_set_boolean (value, priv->ready);
167         break;
168       case PROP_PRESENCE:
169         g_value_set_uint (value, priv->presence);
170         break;
171       case PROP_STATUS:
172         g_value_set_string (value, priv->status);
173         break;
174       case PROP_STATUS_MESSAGE:
175         g_value_set_string (value, priv->message);
176         break;
177       case PROP_CONNECTION_STATUS:
178         g_value_set_uint (value, priv->connection_status);
179         break;
180       case PROP_CONNECTION_STATUS_REASON:
181         g_value_set_uint (value, priv->reason);
182         break;
183       case PROP_CONNECTION:
184         g_value_set_object (value,
185             empathy_account_get_connection (account));
186         break;
187       case PROP_UNIQUE_NAME:
188         g_value_set_string (value,
189             empathy_account_get_unique_name (account));
190         break;
191       case PROP_DISPLAY_NAME:
192         g_value_set_string (value,
193             empathy_account_get_display_name (account));
194         break;
195       case PROP_DBUS_DAEMON:
196         g_value_set_object (value, priv->dbus);
197         break;
198       default:
199         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200         break;
201     }
202 }
203
204 static void
205 empathy_account_update (EmpathyAccount *account,
206     GHashTable *properties)
207 {
208   EmpathyAccountPriv *priv = GET_PRIV (account);
209   GValueArray *arr;
210   TpConnectionStatus old_s = priv->connection_status;
211   gboolean presence_changed = FALSE;
212
213   if (g_hash_table_lookup (properties, "ConnectionStatus") != NULL)
214     priv->connection_status =
215       tp_asv_get_int32 (properties, "ConnectionStatus", NULL);
216
217   if (g_hash_table_lookup (properties, "ConnectionStatusReason") != NULL)
218     priv->reason = tp_asv_get_int32 (properties,
219       "ConnectionStatusReason", NULL);
220
221   if (g_hash_table_lookup (properties, "CurrentPresence") != NULL)
222     {
223       presence_changed = TRUE;
224       arr = tp_asv_get_boxed (properties, "CurrentPresence",
225         TP_STRUCT_TYPE_SIMPLE_PRESENCE);
226       priv->presence = g_value_get_uint (g_value_array_get_nth (arr, 0));
227
228       g_free (priv->status);
229       priv->status = g_value_dup_string (g_value_array_get_nth (arr, 1));
230
231       g_free (priv->message);
232       priv->message = g_value_dup_string (g_value_array_get_nth (arr, 2));
233     }
234
235   if (g_hash_table_lookup (properties, "DisplayName") != NULL)
236     {
237       g_free (priv->display_name);
238       priv->display_name =
239         g_strdup (tp_asv_get_string (properties, "DisplayName"));
240       g_object_notify (G_OBJECT (account), "display-name");
241     }
242
243   if (g_hash_table_lookup (properties, "Enabled") != NULL)
244     {
245       gboolean enabled = tp_asv_get_boolean (properties, "Enabled", NULL);
246       if (priv->enabled != enabled)
247         {
248           priv->enabled = enabled;
249           g_object_notify (G_OBJECT (account), "enabled");
250         }
251     }
252
253   if (g_hash_table_lookup (properties, "Valid") != NULL)
254     priv->valid = tp_asv_get_boolean (properties, "Valid", NULL);
255
256   if (g_hash_table_lookup (properties, "Parameters") != NULL)
257     {
258       GHashTable *parameters;
259
260       parameters = tp_asv_get_boxed (properties, "Parameters",
261         TP_HASH_TYPE_STRING_VARIANT_MAP);
262
263       if (priv->parameters != NULL)
264         g_hash_table_unref (priv->parameters);
265
266       priv->parameters = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP,
267         parameters);
268     }
269
270   if (!priv->ready)
271     {
272       priv->ready = TRUE;
273       g_object_notify (G_OBJECT (account), "ready");
274     }
275
276   if (priv->connection_status != old_s)
277     {
278       if (priv->connection_status == TP_CONNECTION_STATUS_CONNECTED)
279         {
280           GTimeVal val;
281           g_get_current_time (&val);
282
283           priv->connect_time = val.tv_sec;
284         }
285
286       g_signal_emit (account, signals[STATUS_CHANGED], 0,
287         old_s, priv->connection_status, priv->reason);
288
289       g_object_notify (G_OBJECT (account), "connection-status");
290       g_object_notify (G_OBJECT (account), "connection-status-reason");
291     }
292
293   if (presence_changed)
294     {
295       g_signal_emit (account, signals[PRESENCE_CHANGED], 0,
296         priv->presence, priv->status, priv->message);
297       g_object_notify (G_OBJECT (account), "presence");
298       g_object_notify (G_OBJECT (account), "status");
299       g_object_notify (G_OBJECT (account), "status-message");
300     }
301
302   if (g_hash_table_lookup (properties, "Connection") != NULL)
303     {
304       const gchar *conn_path =
305         tp_asv_get_object_path (properties, "Connection");
306
307       _empathy_account_set_connection (account, conn_path);
308     }
309 }
310
311 static void
312 empathy_account_properties_changed (TpAccount *proxy,
313     GHashTable *properties,
314     gpointer user_data,
315     GObject *weak_object)
316 {
317   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
318   EmpathyAccountPriv *priv = GET_PRIV (account);
319
320   if (!priv->ready)
321     return;
322
323   empathy_account_update (account, properties);
324 }
325
326 static void
327 empathy_account_removed_cb (TpAccount *proxy,
328     gpointer user_data,
329     GObject *weak_object)
330 {
331   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
332   EmpathyAccountPriv *priv = GET_PRIV (account);
333
334   if (priv->removed)
335     return;
336
337   priv->removed = TRUE;
338
339   g_signal_emit (account, signals[REMOVED], 0);
340 }
341
342 static void
343 empathy_account_got_all_cb (TpProxy *proxy,
344     GHashTable *properties,
345     const GError *error,
346     gpointer user_data,
347     GObject *weak_object)
348 {
349   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
350
351   DEBUG ("Got whole set of properties for %s",
352     empathy_account_get_unique_name (account));
353
354   if (error != NULL)
355     {
356       DEBUG ("Failed to get the initial set of account properties: %s",
357         error->message);
358       return;
359     }
360
361   empathy_account_update (account, properties);
362 }
363
364 static gchar *
365 empathy_account_unescape_protocol (const gchar *protocol, gssize len)
366 {
367   gchar  *result, *escape;
368   /* Bad implementation might accidentally use tp_escape_as_identifier,
369    * which escapes - in the wrong way... */
370   if ((escape = g_strstr_len (protocol, len, "_2d")) != NULL)
371     {
372       GString *str;
373       const gchar *input;
374
375       str = g_string_new ("");
376       input = protocol;
377       do {
378         g_string_append_len (str, input, escape - input);
379         g_string_append_c (str, '-');
380
381         len -= escape - input + 3;
382         input = escape + 3;
383       } while ((escape = g_strstr_len (input, len, "_2d")) != NULL);
384
385       g_string_append_len (str, input, len);
386
387       result = g_string_free (str, FALSE);
388     }
389   else
390     {
391       result = g_strndup (protocol, len);
392     }
393
394   g_strdelimit (result, "_", '-');
395
396   return result;
397 }
398
399 static gboolean
400 empathy_account_parse_unique_name (const gchar *bus_name,
401     gchar **protocol, gchar **manager)
402 {
403   const gchar *proto, *proto_end;
404   const gchar *cm, *cm_end;
405
406   g_return_val_if_fail (
407     g_str_has_prefix (bus_name, TP_ACCOUNT_OBJECT_PATH_BASE), FALSE);
408
409   cm = bus_name + strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
410
411   for (cm_end = cm; *cm_end != '/' && *cm_end != '\0'; cm_end++)
412     /* pass */;
413
414   if (*cm_end == '\0')
415     return FALSE;
416
417   if (cm_end == '\0')
418     return FALSE;
419
420   proto = cm_end + 1;
421
422   for (proto_end = proto; *proto_end != '/' && *proto_end != '\0'; proto_end++)
423     /* pass */;
424
425   if (*proto_end == '\0')
426     return FALSE;
427
428   if (protocol != NULL)
429     {
430       *protocol = empathy_account_unescape_protocol (proto, proto_end - proto);
431     }
432
433   if (manager != NULL)
434     *manager = g_strndup (cm, cm_end - cm);
435
436   return TRUE;
437 }
438
439 static void
440 account_invalidated_cb (TpProxy *proxy, guint domain, gint code,
441   gchar *message, gpointer user_data)
442 {
443   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
444   EmpathyAccountPriv *priv = GET_PRIV (account);
445
446   if (priv->removed)
447     return;
448
449   priv->removed = TRUE;
450
451   g_signal_emit (account, signals[REMOVED], 0);
452 }
453
454 static void
455 empathy_account_constructed (GObject *object)
456 {
457   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
458   EmpathyAccountPriv *priv = GET_PRIV (account);
459
460   priv->account = tp_account_new (priv->dbus, priv->unique_name, NULL);
461
462   g_signal_connect (priv->account, "invalidated",
463     G_CALLBACK (account_invalidated_cb), object);
464
465   empathy_account_parse_unique_name (priv->unique_name,
466     &(priv->proto_name), &(priv->cm_name));
467
468   priv->icon_name = empathy_protocol_icon_name (priv->proto_name);
469
470   tp_cli_account_connect_to_account_property_changed (priv->account,
471     empathy_account_properties_changed,
472     NULL, NULL, object, NULL);
473
474   tp_cli_account_connect_to_removed (priv->account,
475     empathy_account_removed_cb,
476     NULL, NULL, object, NULL);
477
478   empathy_account_refresh_properties (account);
479 }
480
481 static void empathy_account_dispose (GObject *object);
482 static void empathy_account_finalize (GObject *object);
483
484 static void
485 empathy_account_class_init (EmpathyAccountClass *empathy_account_class)
486 {
487   GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_class);
488
489   g_type_class_add_private (empathy_account_class,
490     sizeof (EmpathyAccountPriv));
491
492   object_class->set_property = empathy_account_set_property;
493   object_class->get_property = empathy_account_get_property;
494   object_class->dispose = empathy_account_dispose;
495   object_class->finalize = empathy_account_finalize;
496   object_class->constructed = empathy_account_constructed;
497
498   g_object_class_install_property (object_class, PROP_ENABLED,
499     g_param_spec_boolean ("enabled",
500       "Enabled",
501       "Whether this account is enabled or not",
502       FALSE,
503       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
504
505   g_object_class_install_property (object_class, PROP_READY,
506     g_param_spec_boolean ("ready",
507       "Ready",
508       "Whether this account is ready to be used",
509       FALSE,
510       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
511
512   g_object_class_install_property (object_class, PROP_PRESENCE,
513     g_param_spec_uint ("presence",
514       "Presence",
515       "The account connections presence type",
516       0,
517       NUM_TP_CONNECTION_PRESENCE_TYPES,
518       TP_CONNECTION_PRESENCE_TYPE_UNSET,
519       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
520
521   g_object_class_install_property (object_class, PROP_STATUS,
522     g_param_spec_string ("status",
523       "Status",
524       "The Status string of the account",
525       NULL,
526       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
527
528   g_object_class_install_property (object_class, PROP_STATUS_MESSAGE,
529     g_param_spec_string ("status-message",
530       "status-message",
531       "The Status message string of the account",
532       NULL,
533       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
534
535   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS,
536     g_param_spec_uint ("connection-status",
537       "ConnectionStatus",
538       "The accounts connections status type",
539       0,
540       NUM_TP_CONNECTION_STATUSES,
541       TP_CONNECTION_STATUS_DISCONNECTED,
542       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
543
544   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS_REASON,
545     g_param_spec_uint ("connection-status-reason",
546       "ConnectionStatusReason",
547       "The account connections status reason",
548       0,
549       NUM_TP_CONNECTION_STATUS_REASONS,
550       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
551       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
552
553   g_object_class_install_property (object_class, PROP_CONNECTION,
554     g_param_spec_object ("connection",
555       "Connection",
556       "The accounts connection",
557       TP_TYPE_CONNECTION,
558       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
559
560   g_object_class_install_property (object_class, PROP_UNIQUE_NAME,
561     g_param_spec_string ("unique-name",
562       "UniqueName",
563       "The accounts unique name",
564       NULL,
565       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
566
567   g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
568     g_param_spec_object ("dbus-daemon",
569       "dbus-daemon",
570       "The Tp Dbus daemon on which this account exists",
571       TP_TYPE_DBUS_DAEMON,
572       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
573
574   g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
575     g_param_spec_string ("display-name",
576       "DisplayName",
577       "The accounts display name",
578       NULL,
579       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
580
581   signals[STATUS_CHANGED] = g_signal_new ("status-changed",
582     G_TYPE_FROM_CLASS (object_class),
583     G_SIGNAL_RUN_LAST,
584     0, NULL, NULL,
585     _empathy_marshal_VOID__UINT_UINT_UINT,
586     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
587
588   signals[PRESENCE_CHANGED] = g_signal_new ("presence-changed",
589     G_TYPE_FROM_CLASS (object_class),
590     G_SIGNAL_RUN_LAST,
591     0, NULL, NULL,
592     _empathy_marshal_VOID__UINT_STRING_STRING,
593     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
594
595   signals[REMOVED] = g_signal_new ("removed",
596     G_TYPE_FROM_CLASS (object_class),
597     G_SIGNAL_RUN_LAST,
598     0, NULL, NULL,
599     g_cclosure_marshal_VOID__VOID,
600     G_TYPE_NONE, 0);
601 }
602
603 static void
604 empathy_account_free_connection (EmpathyAccount *account)
605 {
606   EmpathyAccountPriv *priv = GET_PRIV (account);
607   TpConnection *conn;
608
609   if (priv->connection == NULL)
610     return;
611
612   conn = priv->connection;
613   priv->connection = NULL;
614
615   if (priv->connection_invalidated_id != 0)
616     g_signal_handler_disconnect (conn, priv->connection_invalidated_id);
617   priv->connection_invalidated_id = 0;
618
619   g_object_unref (conn);
620 }
621
622 void
623 empathy_account_dispose (GObject *object)
624 {
625   EmpathyAccount *self = EMPATHY_ACCOUNT (object);
626   EmpathyAccountPriv *priv = GET_PRIV (self);
627
628   if (priv->dispose_has_run)
629     return;
630
631   priv->dispose_has_run = TRUE;
632
633   empathy_account_free_connection (self);
634
635   /* release any references held by the object here */
636   if (G_OBJECT_CLASS (empathy_account_parent_class)->dispose != NULL)
637     G_OBJECT_CLASS (empathy_account_parent_class)->dispose (object);
638 }
639
640 void
641 empathy_account_finalize (GObject *object)
642 {
643   EmpathyAccountPriv *priv = GET_PRIV (object);
644
645   g_free (priv->status);
646   g_free (priv->message);
647
648   g_free (priv->cm_name);
649   g_free (priv->proto_name);
650   g_free (priv->icon_name);
651   g_free (priv->display_name);
652
653   /* free any data held directly by the object here */
654   if (G_OBJECT_CLASS (empathy_account_parent_class)->finalize != NULL)
655     G_OBJECT_CLASS (empathy_account_parent_class)->finalize (object);
656 }
657
658 gboolean
659 empathy_account_is_just_connected (EmpathyAccount *account)
660 {
661   EmpathyAccountPriv *priv = GET_PRIV (account);
662   GTimeVal val;
663
664   if (priv->connection_status != TP_CONNECTION_STATUS_CONNECTED)
665     return FALSE;
666
667   g_get_current_time (&val);
668
669   return (val.tv_sec - priv->connect_time) < 10;
670 }
671
672 /**
673  * empathy_account_get_connection:
674  * @account: a #EmpathyAccount
675  *
676  * Get the connection of the account, or NULL if account is offline or the
677  * connection is not yet ready. This function does not return a new ref.
678  *
679  * Returns: the connection of the account.
680  **/
681 TpConnection *
682 empathy_account_get_connection (EmpathyAccount *account)
683 {
684   EmpathyAccountPriv *priv = GET_PRIV (account);
685
686   if (priv->connection != NULL &&
687       tp_connection_is_ready (priv->connection))
688     return priv->connection;
689
690   return NULL;
691 }
692
693 /**
694  * empathy_account_get_connection_for_path:
695  * @account: a #EmpathyAccount
696  * @patch: the path to connection object for #EmpathyAccount
697  *
698  * Get the connection of the account on path. This function does not return a
699  * new ref. It is not guaranteed that the returned connection object is ready
700  *
701  * Returns: the connection of the account.
702  **/
703 TpConnection *
704 empathy_account_get_connection_for_path (EmpathyAccount *account,
705   const gchar *path)
706 {
707   EmpathyAccountPriv *priv = GET_PRIV (account);
708
709   /* double-check that the object path is valid */
710   if (!tp_dbus_check_valid_object_path (path, NULL))
711     return NULL;
712
713   /* Should be a full object path, not the special "/" value */
714   if (strlen (path) == 1)
715     return NULL;
716
717   _empathy_account_set_connection (account, path);
718
719   return priv->connection;
720 }
721
722 /**
723  * empathy_account_get_unique_name:
724  * @account: a #EmpathyAccount
725  *
726  * Returns: the unique name of the account.
727  **/
728 const gchar *
729 empathy_account_get_unique_name (EmpathyAccount *account)
730 {
731   EmpathyAccountPriv *priv = GET_PRIV (account);
732
733   return priv->unique_name;
734 }
735
736 /**
737  * empathy_account_get_display_name:
738  * @account: a #EmpathyAccount
739  *
740  * Returns: the display name of the account.
741  **/
742 const gchar *
743 empathy_account_get_display_name (EmpathyAccount *account)
744 {
745   EmpathyAccountPriv *priv = GET_PRIV (account);
746
747   return priv->display_name;
748 }
749
750 gboolean
751 empathy_account_is_valid (EmpathyAccount *account)
752 {
753   EmpathyAccountPriv *priv = GET_PRIV (account);
754
755   return priv->valid;
756 }
757
758 const gchar *
759 empathy_account_get_connection_manager (EmpathyAccount *account)
760 {
761   EmpathyAccountPriv *priv = GET_PRIV (account);
762
763   return priv->cm_name;
764 }
765
766 const gchar *
767 empathy_account_get_protocol (EmpathyAccount *account)
768 {
769   EmpathyAccountPriv *priv = GET_PRIV (account);
770
771   return priv->proto_name;
772 }
773
774 const gchar *
775 empathy_account_get_icon_name (EmpathyAccount *account)
776 {
777   EmpathyAccountPriv *priv = GET_PRIV (account);
778
779   return priv->icon_name;
780 }
781
782 const GHashTable *
783 empathy_account_get_parameters (EmpathyAccount *account)
784 {
785   EmpathyAccountPriv *priv = GET_PRIV (account);
786
787   return priv->parameters;
788 }
789
790 gboolean
791 empathy_account_is_enabled (EmpathyAccount *account)
792 {
793   EmpathyAccountPriv *priv = GET_PRIV (account);
794
795   return priv->enabled;
796 }
797
798 gboolean
799 empathy_account_is_ready (EmpathyAccount *account)
800 {
801   EmpathyAccountPriv *priv = GET_PRIV (account);
802
803   return priv->ready;
804 }
805
806
807 EmpathyAccount *
808 empathy_account_new (TpDBusDaemon *dbus,
809     const gchar *unique_name)
810 {
811   return EMPATHY_ACCOUNT (g_object_new (EMPATHY_TYPE_ACCOUNT,
812     "dbus-daemon", dbus,
813     "unique-name", unique_name,
814     NULL));
815 }
816
817 static void
818 empathy_account_connection_ready_cb (TpConnection *connection,
819     const GError *error,
820     gpointer user_data)
821 {
822   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
823
824   if (error != NULL)
825     {
826       DEBUG ("(%s) Connection failed to become ready: %s",
827         empathy_account_get_unique_name (account), error->message);
828       empathy_account_free_connection (account);
829     }
830   else
831     {
832       DEBUG ("(%s) Connection ready",
833         empathy_account_get_unique_name (account));
834       g_object_notify (G_OBJECT (account), "connection");
835     }
836 }
837
838 static void
839 _empathy_account_connection_invalidated_cb (TpProxy *self,
840   guint    domain,
841   gint     code,
842   gchar   *message,
843   gpointer user_data)
844 {
845   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
846   EmpathyAccountPriv *priv = GET_PRIV (account);
847
848   if (priv->connection == NULL)
849     return;
850
851   DEBUG ("(%s) Connection invalidated",
852     empathy_account_get_unique_name (account));
853
854   g_assert (priv->connection == TP_CONNECTION (self));
855
856   empathy_account_free_connection (account);
857
858   g_object_notify (G_OBJECT (account), "connection");
859 }
860
861 static void
862 _empathy_account_set_connection (EmpathyAccount *account,
863     const gchar *path)
864 {
865   EmpathyAccountPriv *priv = GET_PRIV (account);
866
867   if (priv->connection != NULL)
868     {
869       const gchar *current;
870
871       current = tp_proxy_get_object_path (priv->connection);
872       if (!tp_strdiff (current, path))
873         return;
874     }
875
876   empathy_account_free_connection (account);
877
878   if (tp_strdiff ("/", path))
879     {
880       GError *error = NULL;
881       priv->connection = tp_connection_new (priv->dbus, NULL, path, &error);
882
883       if (priv->connection == NULL)
884         {
885           DEBUG ("Failed to create a new TpConnection: %s",
886                 error->message);
887           g_error_free (error);
888         }
889       else
890         {
891           priv->connection_invalidated_id = g_signal_connect (priv->connection,
892             "invalidated",
893             G_CALLBACK (_empathy_account_connection_invalidated_cb), account);
894
895           DEBUG ("Readying connection for %s", priv->unique_name);
896           /* notify a change in the connection property when it's ready */
897           tp_connection_call_when_ready (priv->connection,
898             empathy_account_connection_ready_cb, account);
899         }
900     }
901
902    g_object_notify (G_OBJECT (account), "connection");
903 }
904
905 static void
906 account_enabled_set_cb (TpProxy *proxy,
907     const GError *error,
908     gpointer user_data,
909     GObject *weak_object)
910 {
911   GSimpleAsyncResult *result = user_data;
912
913   if (error != NULL)
914     g_simple_async_result_set_from_error (result, (GError *) error);
915
916   g_simple_async_result_complete (result);
917   g_object_unref (result);
918 }
919
920 gboolean
921 empathy_account_set_enabled_finish (EmpathyAccount *account,
922     GAsyncResult *result,
923     GError **error)
924 {
925   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
926           error) ||
927       !g_simple_async_result_is_valid (result, G_OBJECT (account),
928           empathy_account_set_enabled_finish))
929     return FALSE;
930
931   return TRUE;
932 }
933
934 void
935 empathy_account_set_enabled_async (EmpathyAccount *account,
936     gboolean enabled,
937     GAsyncReadyCallback callback,
938     gpointer user_data)
939 {
940   EmpathyAccountPriv *priv = GET_PRIV (account);
941   EmpathyAccountManager *acc_manager;
942   GValue value = {0, };
943   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
944       callback, user_data, empathy_account_set_enabled_finish);
945   char *status = NULL;
946   char *status_message = NULL;
947   TpConnectionPresenceType presence;
948
949   if (priv->enabled == enabled)
950     {
951       g_simple_async_result_complete_in_idle (result);
952       return;
953     }
954
955   if (enabled)
956     {
957       acc_manager = empathy_account_manager_dup_singleton ();
958       presence = empathy_account_manager_get_requested_global_presence
959         (acc_manager, &status, &status_message);
960
961       if (presence != TP_CONNECTION_PRESENCE_TYPE_UNSET)
962         empathy_account_request_presence (account, presence, status,
963             status_message);
964
965       g_object_unref (acc_manager);
966       g_free (status);
967       g_free (status_message);
968     }
969
970   g_value_init (&value, G_TYPE_BOOLEAN);
971   g_value_set_boolean (&value, enabled);
972
973   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
974       -1, TP_IFACE_ACCOUNT, "Enabled", &value,
975       account_enabled_set_cb, result, NULL, G_OBJECT (account));
976 }
977
978 static void
979 account_reconnected_cb (TpAccount *proxy,
980     const GError *error,
981     gpointer user_data,
982     GObject *weak_object)
983 {
984   GSimpleAsyncResult *result = user_data;
985
986   if (error != NULL)
987     g_simple_async_result_set_from_error (result, (GError *) error);
988
989   g_simple_async_result_complete (result);
990   g_object_unref (result);
991 }
992
993 gboolean
994 empathy_account_reconnect_finish (EmpathyAccount *account,
995     GAsyncResult *result,
996     GError **error)
997 {
998   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
999           error) ||
1000       !g_simple_async_result_is_valid (result, G_OBJECT (account),
1001           empathy_account_reconnect_finish))
1002     return FALSE;
1003
1004   return TRUE;
1005 }
1006
1007 void
1008 empathy_account_reconnect_async (EmpathyAccount *account,
1009     GAsyncReadyCallback callback,
1010     gpointer user_data)
1011 {
1012   EmpathyAccountPriv *priv = GET_PRIV (account);
1013
1014   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
1015         callback, user_data, empathy_account_reconnect_finish);
1016
1017   tp_cli_account_call_reconnect (priv->account,
1018       -1, account_reconnected_cb, result, NULL, G_OBJECT (account));
1019 }
1020
1021 static void
1022 empathy_account_requested_presence_cb (TpProxy *proxy,
1023   const GError *error,
1024   gpointer user_data,
1025   GObject *weak_object)
1026 {
1027   if (error)
1028     DEBUG ("Failed to set the requested presence: %s", error->message);
1029 }
1030
1031
1032 void
1033 empathy_account_request_presence (EmpathyAccount *account,
1034   TpConnectionPresenceType type,
1035   const gchar *status,
1036   const gchar *message)
1037 {
1038   EmpathyAccountPriv *priv = GET_PRIV (account);
1039   GValue value = {0, };
1040   GValueArray *arr;
1041
1042   g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
1043   g_value_take_boxed (&value, dbus_g_type_specialized_construct
1044     (TP_STRUCT_TYPE_SIMPLE_PRESENCE));
1045   arr = (GValueArray *) g_value_get_boxed (&value);
1046
1047   g_value_set_uint (arr->values, type);
1048   g_value_set_static_string (arr->values + 1, status);
1049   g_value_set_static_string (arr->values + 2, message);
1050
1051   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
1052     -1,
1053     TP_IFACE_ACCOUNT,
1054     "RequestedPresence",
1055     &value,
1056     empathy_account_requested_presence_cb,
1057     NULL,
1058     NULL,
1059     G_OBJECT (account));
1060
1061   g_value_unset (&value);
1062 }
1063
1064 static void
1065 empathy_account_updated_cb (TpAccount *proxy,
1066     const gchar **reconnect_required,
1067     const GError *error,
1068     gpointer user_data,
1069     GObject *weak_object)
1070 {
1071   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
1072
1073   if (error != NULL)
1074     {
1075       g_simple_async_result_set_from_error (result, (GError *) error);
1076     }
1077
1078   g_simple_async_result_complete (result);
1079   g_object_unref (G_OBJECT (result));
1080 }
1081
1082 void
1083 empathy_account_update_settings_async (EmpathyAccount *account,
1084   GHashTable *parameters, const gchar **unset_parameters,
1085   GAsyncReadyCallback callback, gpointer user_data)
1086 {
1087   EmpathyAccountPriv *priv = GET_PRIV (account);
1088   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
1089       callback, user_data, empathy_account_update_settings_finish);
1090
1091   tp_cli_account_call_update_parameters (priv->account,
1092       -1,
1093       parameters,
1094       unset_parameters,
1095       empathy_account_updated_cb,
1096       result,
1097       NULL,
1098       G_OBJECT (account));
1099 }
1100
1101 gboolean
1102 empathy_account_update_settings_finish (EmpathyAccount *account,
1103   GAsyncResult *result, GError **error)
1104 {
1105   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1106       error))
1107     return FALSE;
1108
1109   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1110     G_OBJECT (account), empathy_account_update_settings_finish), FALSE);
1111
1112   return TRUE;
1113 }
1114
1115 static void
1116 account_display_name_set_cb (TpProxy *proxy,
1117     const GError *error,
1118     gpointer user_data,
1119     GObject *weak_object)
1120 {
1121   GSimpleAsyncResult *result = user_data;
1122
1123   if (error != NULL)
1124     g_simple_async_result_set_from_error (result, (GError *) error);
1125
1126   g_simple_async_result_complete (result);
1127   g_object_unref (result);
1128 }
1129
1130 void
1131 empathy_account_set_display_name_async (EmpathyAccount *account,
1132     const char *display_name,
1133     GAsyncReadyCallback callback,
1134     gpointer user_data)
1135 {
1136   GSimpleAsyncResult *result;
1137   GValue value = {0, };
1138   EmpathyAccountPriv *priv = GET_PRIV (account);
1139
1140   if (display_name == NULL)
1141     {
1142       g_simple_async_report_error_in_idle (G_OBJECT (account),
1143           callback, user_data, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1144           _("Can't set an empty display name"));
1145       return;
1146     }
1147
1148   result = g_simple_async_result_new (G_OBJECT (account), callback,
1149       user_data, empathy_account_set_display_name_finish);
1150
1151   g_value_init (&value, G_TYPE_STRING);
1152   g_value_set_string (&value, display_name);
1153
1154   tp_cli_dbus_properties_call_set (priv->account, -1, TP_IFACE_ACCOUNT,
1155       "DisplayName", &value, account_display_name_set_cb, result, NULL,
1156       G_OBJECT (account));
1157 }
1158
1159 gboolean
1160 empathy_account_set_display_name_finish (EmpathyAccount *account,
1161     GAsyncResult *result, GError **error)
1162 {
1163   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1164           error) ||
1165       !g_simple_async_result_is_valid (result, G_OBJECT (account),
1166           empathy_account_set_display_name_finish))
1167     return FALSE;
1168
1169   return TRUE;
1170 }
1171
1172 static void
1173 empathy_account_remove_cb (TpAccount *proxy,
1174     const GError *error,
1175     gpointer user_data,
1176     GObject *weak_object)
1177 {
1178   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
1179
1180   if (error != NULL)
1181     {
1182       g_simple_async_result_set_from_error (result, (GError *) error);
1183     }
1184
1185   g_simple_async_result_complete (result);
1186   g_object_unref (G_OBJECT (result));
1187 }
1188
1189 void
1190 empathy_account_remove_async (EmpathyAccount *account,
1191   GAsyncReadyCallback callback, gpointer user_data)
1192 {
1193   EmpathyAccountPriv *priv = GET_PRIV (account);
1194   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
1195       callback, user_data, empathy_account_remove_finish);
1196
1197   tp_cli_account_call_remove (priv->account,
1198       -1,
1199       empathy_account_remove_cb,
1200       result,
1201       NULL,
1202       G_OBJECT (account));
1203 }
1204
1205 gboolean
1206 empathy_account_remove_finish (EmpathyAccount *account,
1207   GAsyncResult *result, GError **error)
1208 {
1209   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1210       error))
1211     return FALSE;
1212
1213   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1214     G_OBJECT (account), empathy_account_update_settings_finish), FALSE);
1215
1216   return TRUE;
1217 }
1218
1219 void
1220 empathy_account_refresh_properties (EmpathyAccount *account)
1221 {
1222   EmpathyAccountPriv *priv;
1223
1224   g_return_if_fail (EMPATHY_IS_ACCOUNT (account));
1225
1226   priv = GET_PRIV (account);
1227
1228   tp_cli_dbus_properties_call_get_all (priv->account, -1,
1229     TP_IFACE_ACCOUNT,
1230     empathy_account_got_all_cb,
1231     NULL,
1232     NULL,
1233     G_OBJECT (account));
1234 }