]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account.c
Merge branch 'mc5', fixes bug #590165
[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-utils.h"
40 #include "empathy-marshal.h"
41
42 /* signals */
43 enum {
44   STATUS_CHANGED,
45   PRESENCE_CHANGED,
46   REMOVED,
47   LAST_SIGNAL
48 };
49
50 static guint signals[LAST_SIGNAL];
51
52 /* properties */
53 enum {
54   PROP_ENABLED = 1,
55   PROP_PRESENCE,
56   PROP_STATUS,
57   PROP_STATUS_MESSAGE,
58   PROP_READY,
59   PROP_CONNECTION_STATUS,
60   PROP_CONNECTION_STATUS_REASON,
61   PROP_CONNECTION,
62   PROP_UNIQUE_NAME,
63   PROP_DBUS_DAEMON,
64   PROP_DISPLAY_NAME
65 };
66
67 G_DEFINE_TYPE(EmpathyAccount, empathy_account, G_TYPE_OBJECT)
68
69 /* private structure */
70 typedef struct _EmpathyAccountPriv EmpathyAccountPriv;
71
72 struct _EmpathyAccountPriv
73 {
74   gboolean dispose_has_run;
75
76   TpConnection *connection;
77   guint connection_invalidated_id;
78
79   TpConnectionStatus connection_status;
80   TpConnectionStatusReason reason;
81
82   TpConnectionPresenceType presence;
83   gchar *status;
84   gchar *message;
85
86   gboolean enabled;
87   gboolean valid;
88   gboolean ready;
89   gboolean removed;
90   /* Timestamp when the connection got connected in seconds since the epoch */
91   glong connect_time;
92
93   gchar *cm_name;
94   gchar *proto_name;
95   gchar *icon_name;
96
97   gchar *unique_name;
98   gchar *display_name;
99   TpDBusDaemon *dbus;
100
101   TpAccount *account;
102   GHashTable *parameters;
103 };
104
105 #define GET_PRIV(obj)  EMPATHY_GET_PRIV (obj, EmpathyAccount)
106
107 static void _empathy_account_set_connection (EmpathyAccount *account,
108     const gchar *path);
109
110 static void
111 empathy_account_init (EmpathyAccount *obj)
112 {
113   EmpathyAccountPriv *priv;
114
115   priv =  G_TYPE_INSTANCE_GET_PRIVATE (obj,
116     EMPATHY_TYPE_ACCOUNT, EmpathyAccountPriv);
117
118   obj->priv = priv;
119
120   priv->connection_status = TP_CONNECTION_STATUS_DISCONNECTED;
121 }
122
123 static void
124 empathy_account_set_property (GObject *object,
125     guint prop_id,
126     const GValue *value,
127     GParamSpec *pspec)
128 {
129   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
130   EmpathyAccountPriv *priv = GET_PRIV (account);
131
132   switch (prop_id)
133     {
134       case PROP_ENABLED:
135         empathy_account_set_enabled_async (account,
136             g_value_get_boolean (value), NULL, NULL);
137         break;
138       case PROP_UNIQUE_NAME:
139         priv->unique_name = g_value_dup_string (value);
140         break;
141       case PROP_DBUS_DAEMON:
142         priv->dbus = g_value_get_object (value);
143         break;
144       default:
145         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
146         break;
147     }
148 }
149
150 static void
151 empathy_account_get_property (GObject *object,
152     guint prop_id,
153     GValue *value,
154     GParamSpec *pspec)
155 {
156   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
157   EmpathyAccountPriv *priv = GET_PRIV (account);
158
159   switch (prop_id)
160     {
161       case PROP_ENABLED:
162         g_value_set_boolean (value, priv->enabled);
163         break;
164       case PROP_READY:
165         g_value_set_boolean (value, priv->ready);
166         break;
167       case PROP_PRESENCE:
168         g_value_set_uint (value, priv->presence);
169         break;
170       case PROP_STATUS:
171         g_value_set_string (value, priv->status);
172         break;
173       case PROP_STATUS_MESSAGE:
174         g_value_set_string (value, priv->message);
175         break;
176       case PROP_CONNECTION_STATUS:
177         g_value_set_uint (value, priv->connection_status);
178         break;
179       case PROP_CONNECTION_STATUS_REASON:
180         g_value_set_uint (value, priv->reason);
181         break;
182       case PROP_CONNECTION:
183         g_value_set_object (value,
184             empathy_account_get_connection (account));
185         break;
186       case PROP_UNIQUE_NAME:
187         g_value_set_string (value,
188             empathy_account_get_unique_name (account));
189         break;
190       case PROP_DISPLAY_NAME:
191         g_value_set_string (value,
192             empathy_account_get_display_name (account));
193         break;
194       case PROP_DBUS_DAEMON:
195         g_value_set_object (value, priv->dbus);
196         break;
197       default:
198         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
199         break;
200     }
201 }
202
203 static void
204 empathy_account_update (EmpathyAccount *account,
205     GHashTable *properties)
206 {
207   EmpathyAccountPriv *priv = GET_PRIV (account);
208   GValueArray *arr;
209   TpConnectionStatus old_s = priv->connection_status;
210   gboolean presence_changed = FALSE;
211
212   if (g_hash_table_lookup (properties, "ConnectionStatus") != NULL)
213     priv->connection_status =
214       tp_asv_get_int32 (properties, "ConnectionStatus", NULL);
215
216   if (g_hash_table_lookup (properties, "ConnectionStatusReason") != NULL)
217     priv->reason = tp_asv_get_int32 (properties,
218       "ConnectionStatusReason", NULL);
219
220   if (g_hash_table_lookup (properties, "CurrentPresence") != NULL)
221     {
222       presence_changed = TRUE;
223       arr = tp_asv_get_boxed (properties, "CurrentPresence",
224         TP_STRUCT_TYPE_SIMPLE_PRESENCE);
225       priv->presence = g_value_get_uint (g_value_array_get_nth (arr, 0));
226
227       g_free (priv->status);
228       priv->status = g_value_dup_string (g_value_array_get_nth (arr, 1));
229
230       g_free (priv->message);
231       priv->message = g_value_dup_string (g_value_array_get_nth (arr, 2));
232     }
233
234   if (g_hash_table_lookup (properties, "DisplayName") != NULL)
235     {
236       g_free (priv->display_name);
237       priv->display_name =
238         g_strdup (tp_asv_get_string (properties, "DisplayName"));
239       g_object_notify (G_OBJECT (account), "display-name");
240     }
241
242   if (g_hash_table_lookup (properties, "Enabled") != NULL)
243     {
244       gboolean enabled = tp_asv_get_boolean (properties, "Enabled", NULL);
245       if (priv->enabled != enabled)
246         {
247           priv->enabled = enabled;
248           g_object_notify (G_OBJECT (account), "enabled");
249         }
250     }
251
252   if (g_hash_table_lookup (properties, "Valid") != NULL)
253     priv->valid = tp_asv_get_boolean (properties, "Valid", NULL);
254
255   if (g_hash_table_lookup (properties, "Parameters") != NULL)
256     {
257       GHashTable *parameters;
258
259       parameters = tp_asv_get_boxed (properties, "Parameters",
260         TP_HASH_TYPE_STRING_VARIANT_MAP);
261
262       if (priv->parameters != NULL)
263         g_hash_table_unref (priv->parameters);
264
265       priv->parameters = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP,
266         parameters);
267     }
268
269   if (!priv->ready)
270     {
271       priv->ready = TRUE;
272       g_object_notify (G_OBJECT (account), "ready");
273     }
274
275   if (priv->connection_status != old_s)
276     {
277       if (priv->connection_status == TP_CONNECTION_STATUS_CONNECTED)
278         {
279           GTimeVal val;
280           g_get_current_time (&val);
281
282           priv->connect_time = val.tv_sec;
283         }
284
285       g_signal_emit (account, signals[STATUS_CHANGED], 0,
286         old_s, priv->connection_status, priv->reason);
287
288       g_object_notify (G_OBJECT (account), "connection-status");
289       g_object_notify (G_OBJECT (account), "connection-status-reason");
290     }
291
292   if (presence_changed)
293     {
294       g_signal_emit (account, signals[PRESENCE_CHANGED], 0,
295         priv->presence, priv->status, priv->message);
296       g_object_notify (G_OBJECT (account), "presence");
297       g_object_notify (G_OBJECT (account), "status");
298       g_object_notify (G_OBJECT (account), "status-message");
299     }
300
301   if (g_hash_table_lookup (properties, "Connection") != NULL)
302     {
303       const gchar *conn_path =
304         tp_asv_get_object_path (properties, "Connection");
305
306       _empathy_account_set_connection (account, conn_path);
307     }
308 }
309
310 static void
311 empathy_account_properties_changed (TpAccount *proxy,
312     GHashTable *properties,
313     gpointer user_data,
314     GObject *weak_object)
315 {
316   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
317   EmpathyAccountPriv *priv = GET_PRIV (account);
318
319   if (!priv->ready)
320     return;
321
322   empathy_account_update (account, properties);
323 }
324
325 static void
326 empathy_account_removed_cb (TpAccount *proxy,
327     gpointer user_data,
328     GObject *weak_object)
329 {
330   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
331   EmpathyAccountPriv *priv = GET_PRIV (account);
332
333   if (priv->removed)
334     return;
335
336   priv->removed = TRUE;
337
338   g_signal_emit (account, signals[REMOVED], 0);
339 }
340
341 static void
342 empathy_account_got_all_cb (TpProxy *proxy,
343     GHashTable *properties,
344     const GError *error,
345     gpointer user_data,
346     GObject *weak_object)
347 {
348   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
349
350   DEBUG ("Got initial set of properties for %s",
351     empathy_account_get_unique_name (account));
352
353   if (error != NULL)
354     {
355       DEBUG ("Failed to get the initial set of account properties: %s",
356         error->message);
357       return;
358     }
359
360   empathy_account_update (account, properties);
361 }
362
363 static gchar *
364 empathy_account_unescape_protocol (const gchar *protocol, gssize len)
365 {
366   gchar  *result, *escape;
367   /* Bad implementation might accidentally use tp_escape_as_identifier,
368    * which escapes - in the wrong way... */
369   if ((escape = g_strstr_len (protocol, len, "_2d")) != NULL)
370     {
371       GString *str;
372       const gchar *input;
373
374       str = g_string_new ("");
375       input = protocol;
376       do {
377         g_string_append_len (str, input, escape - input);
378         g_string_append_c (str, '-');
379
380         len -= escape - input + 3;
381         input = escape + 3;
382       } while ((escape = g_strstr_len (input, len, "_2d")) != NULL);
383
384       g_string_append_len (str, input, len);
385
386       result = g_string_free (str, FALSE);
387     }
388   else
389     {
390       result = g_strndup (protocol, len);
391     }
392
393   g_strdelimit (result, "_", '-');
394
395   return result;
396 }
397
398 static gboolean
399 empathy_account_parse_unique_name (const gchar *bus_name,
400     gchar **protocol, gchar **manager)
401 {
402   const gchar *proto, *proto_end;
403   const gchar *cm, *cm_end;
404
405   g_return_val_if_fail (
406     g_str_has_prefix (bus_name, TP_ACCOUNT_OBJECT_PATH_BASE), FALSE);
407
408   cm = bus_name + strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
409
410   for (cm_end = cm; *cm_end != '/' && *cm_end != '\0'; cm_end++)
411     /* pass */;
412
413   if (*cm_end == '\0')
414     return FALSE;
415
416   if (cm_end == '\0')
417     return FALSE;
418
419   proto = cm_end + 1;
420
421   for (proto_end = proto; *proto_end != '/' && *proto_end != '\0'; proto_end++)
422     /* pass */;
423
424   if (*proto_end == '\0')
425     return FALSE;
426
427   if (protocol != NULL)
428     {
429       *protocol = empathy_account_unescape_protocol (proto, proto_end - proto);
430     }
431
432   if (manager != NULL)
433     *manager = g_strndup (cm, cm_end - cm);
434
435   return TRUE;
436 }
437
438 static void
439 account_invalidated_cb (TpProxy *proxy, guint domain, gint code,
440   gchar *message, gpointer user_data)
441 {
442   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
443   EmpathyAccountPriv *priv = GET_PRIV (account);
444
445   if (priv->removed)
446     return;
447
448   priv->removed = TRUE;
449
450   g_signal_emit (account, signals[REMOVED], 0);
451 }
452
453 static void
454 empathy_account_constructed (GObject *object)
455 {
456   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
457   EmpathyAccountPriv *priv = GET_PRIV (account);
458
459   priv->account = tp_account_new (priv->dbus, priv->unique_name, NULL);
460
461   g_signal_connect (priv->account, "invalidated",
462     G_CALLBACK (account_invalidated_cb), object);
463
464   empathy_account_parse_unique_name (priv->unique_name,
465     &(priv->proto_name), &(priv->cm_name));
466
467   priv->icon_name = empathy_protocol_icon_name (priv->proto_name);
468
469   tp_cli_account_connect_to_account_property_changed (priv->account,
470     empathy_account_properties_changed,
471     NULL, NULL, object, NULL);
472
473   tp_cli_account_connect_to_removed (priv->account,
474     empathy_account_removed_cb,
475     NULL, NULL, object, NULL);
476
477   tp_cli_dbus_properties_call_get_all (priv->account, -1,
478     TP_IFACE_ACCOUNT,
479     empathy_account_got_all_cb,
480     NULL,
481     NULL,
482     G_OBJECT (account));
483 }
484
485 static void empathy_account_dispose (GObject *object);
486 static void empathy_account_finalize (GObject *object);
487
488 static void
489 empathy_account_class_init (EmpathyAccountClass *empathy_account_class)
490 {
491   GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_class);
492
493   g_type_class_add_private (empathy_account_class,
494     sizeof (EmpathyAccountPriv));
495
496   object_class->set_property = empathy_account_set_property;
497   object_class->get_property = empathy_account_get_property;
498   object_class->dispose = empathy_account_dispose;
499   object_class->finalize = empathy_account_finalize;
500   object_class->constructed = empathy_account_constructed;
501
502   g_object_class_install_property (object_class, PROP_ENABLED,
503     g_param_spec_boolean ("enabled",
504       "Enabled",
505       "Whether this account is enabled or not",
506       FALSE,
507       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
508
509   g_object_class_install_property (object_class, PROP_READY,
510     g_param_spec_boolean ("ready",
511       "Ready",
512       "Whether this account is ready to be used",
513       FALSE,
514       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
515
516   g_object_class_install_property (object_class, PROP_PRESENCE,
517     g_param_spec_uint ("presence",
518       "Presence",
519       "The account connections presence type",
520       0,
521       NUM_TP_CONNECTION_PRESENCE_TYPES,
522       TP_CONNECTION_PRESENCE_TYPE_UNSET,
523       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
524
525   g_object_class_install_property (object_class, PROP_STATUS,
526     g_param_spec_string ("status",
527       "Status",
528       "The Status string of the account",
529       NULL,
530       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
531
532   g_object_class_install_property (object_class, PROP_STATUS_MESSAGE,
533     g_param_spec_string ("status-message",
534       "status-message",
535       "The Status message string of the account",
536       NULL,
537       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
538
539   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS,
540     g_param_spec_uint ("connection-status",
541       "ConnectionStatus",
542       "The accounts connections status type",
543       0,
544       NUM_TP_CONNECTION_STATUSES,
545       TP_CONNECTION_STATUS_DISCONNECTED,
546       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
547
548   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS_REASON,
549     g_param_spec_uint ("connection-status-reason",
550       "ConnectionStatusReason",
551       "The account connections status reason",
552       0,
553       NUM_TP_CONNECTION_STATUS_REASONS,
554       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
555       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
556
557   g_object_class_install_property (object_class, PROP_CONNECTION,
558     g_param_spec_object ("connection",
559       "Connection",
560       "The accounts connection",
561       TP_TYPE_CONNECTION,
562       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
563
564   g_object_class_install_property (object_class, PROP_UNIQUE_NAME,
565     g_param_spec_string ("unique-name",
566       "UniqueName",
567       "The accounts unique name",
568       NULL,
569       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
570
571   g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
572     g_param_spec_object ("dbus-daemon",
573       "dbus-daemon",
574       "The Tp Dbus daemon on which this account exists",
575       TP_TYPE_DBUS_DAEMON,
576       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
577
578   g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
579     g_param_spec_string ("display-name",
580       "DisplayName",
581       "The accounts display name",
582       NULL,
583       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
584
585   signals[STATUS_CHANGED] = g_signal_new ("status-changed",
586     G_TYPE_FROM_CLASS (object_class),
587     G_SIGNAL_RUN_LAST,
588     0, NULL, NULL,
589     _empathy_marshal_VOID__UINT_UINT_UINT,
590     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
591
592   signals[PRESENCE_CHANGED] = g_signal_new ("presence-changed",
593     G_TYPE_FROM_CLASS (object_class),
594     G_SIGNAL_RUN_LAST,
595     0, NULL, NULL,
596     _empathy_marshal_VOID__UINT_STRING_STRING,
597     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
598
599   signals[REMOVED] = g_signal_new ("removed",
600     G_TYPE_FROM_CLASS (object_class),
601     G_SIGNAL_RUN_LAST,
602     0, NULL, NULL,
603     g_cclosure_marshal_VOID__VOID,
604     G_TYPE_NONE, 0);
605 }
606
607 static void
608 empathy_account_free_connection (EmpathyAccount *account)
609 {
610   EmpathyAccountPriv *priv = GET_PRIV (account);
611
612   if (priv->connection_invalidated_id != 0)
613     g_signal_handler_disconnect (priv->connection,
614         priv->connection_invalidated_id);
615   priv->connection_invalidated_id = 0;
616
617   if (priv->connection != NULL)
618     g_object_unref (priv->connection);
619   priv->connection = NULL;
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   g_signal_handler_disconnect (priv->connection,
857     priv->connection_invalidated_id);
858   priv->connection_invalidated_id = 0;
859
860   g_object_unref (priv->connection);
861   priv->connection = NULL;
862
863   g_object_notify (G_OBJECT (account), "connection");
864 }
865
866 static void
867 _empathy_account_set_connection (EmpathyAccount *account,
868     const gchar *path)
869 {
870   EmpathyAccountPriv *priv = GET_PRIV (account);
871
872   if (priv->connection != NULL)
873     {
874       const gchar *current;
875
876       current = tp_proxy_get_object_path (priv->connection);
877       if (!tp_strdiff (current, path))
878         return;
879     }
880
881   if (priv->connection != NULL)
882     {
883       g_signal_handler_disconnect (priv->connection,
884         priv->connection_invalidated_id);
885       priv->connection_invalidated_id = 0;
886
887       g_object_unref (priv->connection);
888       priv->connection = NULL;
889     }
890
891   if (tp_strdiff ("/", path))
892     {
893       GError *error = NULL;
894       priv->connection = tp_connection_new (priv->dbus, NULL, path, &error);
895
896       if (priv->connection == NULL)
897         {
898           DEBUG ("Failed to create a new TpConnection: %s",
899                 error->message);
900           g_error_free (error);
901         }
902       else
903         {
904           priv->connection_invalidated_id = g_signal_connect (priv->connection,
905             "invalidated",
906             G_CALLBACK (_empathy_account_connection_invalidated_cb), account);
907
908           DEBUG ("Readying connection for %s", priv->unique_name);
909           /* notify a change in the connection property when it's ready */
910           tp_connection_call_when_ready (priv->connection,
911             empathy_account_connection_ready_cb, account);
912         }
913     }
914
915    g_object_notify (G_OBJECT (account), "connection");
916 }
917
918 static void
919 account_enabled_set_cb (TpProxy *proxy,
920     const GError *error,
921     gpointer user_data,
922     GObject *weak_object)
923 {
924   GSimpleAsyncResult *result = user_data;
925
926   if (error != NULL)
927     g_simple_async_result_set_from_error (result, (GError *) error);
928
929   g_simple_async_result_complete (result);
930   g_object_unref (result);
931 }
932
933 gboolean
934 empathy_account_set_enabled_finish (EmpathyAccount *account,
935     GAsyncResult *result,
936     GError **error)
937 {
938   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
939           error) ||
940       !g_simple_async_result_is_valid (result, G_OBJECT (account),
941           empathy_account_set_enabled_finish))
942     return FALSE;
943
944   return TRUE;
945 }
946
947 void
948 empathy_account_set_enabled_async (EmpathyAccount *account,
949     gboolean enabled,
950     GAsyncReadyCallback callback,
951     gpointer user_data)
952 {
953   EmpathyAccountPriv *priv = GET_PRIV (account);
954   GValue value = {0, };
955   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
956       callback, user_data, empathy_account_set_enabled_finish);
957
958   if (priv->enabled == enabled)
959     {
960       g_simple_async_result_complete_in_idle (result);
961       return;
962     }
963
964   g_value_init (&value, G_TYPE_BOOLEAN);
965   g_value_set_boolean (&value, enabled);
966
967   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
968       -1, TP_IFACE_ACCOUNT, "Enabled", &value,
969       account_enabled_set_cb, result, NULL, G_OBJECT (account));
970 }
971
972 static void
973 empathy_account_requested_presence_cb (TpProxy *proxy,
974   const GError *error,
975   gpointer user_data,
976   GObject *weak_object)
977 {
978   if (error)
979     DEBUG ("Failed to set the requested presence: %s", error->message);
980 }
981
982
983 void
984 empathy_account_request_presence (EmpathyAccount *account,
985   TpConnectionPresenceType type,
986   const gchar *status,
987   const gchar *message)
988 {
989   EmpathyAccountPriv *priv = GET_PRIV (account);
990   GValue value = {0, };
991   GValueArray *arr;
992
993   g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
994   g_value_take_boxed (&value, dbus_g_type_specialized_construct
995     (TP_STRUCT_TYPE_SIMPLE_PRESENCE));
996   arr = (GValueArray *) g_value_get_boxed (&value);
997
998   g_value_set_uint (arr->values, type);
999   g_value_set_static_string (arr->values + 1, status);
1000   g_value_set_static_string (arr->values + 2, message);
1001
1002   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
1003     -1,
1004     TP_IFACE_ACCOUNT,
1005     "RequestedPresence",
1006     &value,
1007     empathy_account_requested_presence_cb,
1008     NULL,
1009     NULL,
1010     G_OBJECT (account));
1011
1012   g_value_unset (&value);
1013 }
1014
1015 static void
1016 empathy_account_updated_cb (TpAccount *proxy,
1017     const gchar **reconnect_required,
1018     const GError *error,
1019     gpointer user_data,
1020     GObject *weak_object)
1021 {
1022   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
1023
1024   if (error != NULL)
1025     {
1026       g_simple_async_result_set_from_error (result, (GError *) error);
1027     }
1028
1029   g_simple_async_result_complete (result);
1030   g_object_unref (G_OBJECT (result));
1031 }
1032
1033 void
1034 empathy_account_update_settings_async (EmpathyAccount *account,
1035   GHashTable *parameters, const gchar **unset_parameters,
1036   GAsyncReadyCallback callback, gpointer user_data)
1037 {
1038   EmpathyAccountPriv *priv = GET_PRIV (account);
1039   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
1040       callback, user_data, empathy_account_update_settings_finish);
1041
1042   tp_cli_account_call_update_parameters (priv->account,
1043       -1,
1044       parameters,
1045       unset_parameters,
1046       empathy_account_updated_cb,
1047       result,
1048       NULL,
1049       G_OBJECT (account));
1050 }
1051
1052 gboolean
1053 empathy_account_update_settings_finish (EmpathyAccount *account,
1054   GAsyncResult *result, GError **error)
1055 {
1056   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1057       error))
1058     return FALSE;
1059
1060   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1061     G_OBJECT (account), empathy_account_update_settings_finish), FALSE);
1062
1063   return TRUE;
1064 }
1065
1066 static void
1067 account_display_name_set_cb (TpProxy *proxy,
1068     const GError *error,
1069     gpointer user_data,
1070     GObject *weak_object)
1071 {
1072   GSimpleAsyncResult *result = user_data;
1073
1074   if (error != NULL)
1075     g_simple_async_result_set_from_error (result, (GError *) error);
1076
1077   g_simple_async_result_complete (result);
1078   g_object_unref (result);
1079 }
1080
1081 void
1082 empathy_account_set_display_name_async (EmpathyAccount *account,
1083     const char *display_name,
1084     GAsyncReadyCallback callback,
1085     gpointer user_data)
1086 {
1087   GSimpleAsyncResult *result;
1088   GValue value = {0, };
1089   EmpathyAccountPriv *priv = GET_PRIV (account);
1090
1091   if (display_name == NULL)
1092     {
1093       g_simple_async_report_error_in_idle (G_OBJECT (account),
1094           callback, user_data, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1095           _("Can't set an empty display name"));
1096       return;
1097     }
1098
1099   result = g_simple_async_result_new (G_OBJECT (account), callback,
1100       user_data, empathy_account_set_display_name_finish);
1101
1102   g_value_init (&value, G_TYPE_STRING);
1103   g_value_set_string (&value, display_name);
1104
1105   tp_cli_dbus_properties_call_set (priv->account, -1, TP_IFACE_ACCOUNT,
1106       "DisplayName", &value, account_display_name_set_cb, result, NULL,
1107       G_OBJECT (account));
1108 }
1109
1110 gboolean
1111 empathy_account_set_display_name_finish (EmpathyAccount *account,
1112     GAsyncResult *result, GError **error)
1113 {
1114   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1115           error) ||
1116       !g_simple_async_result_is_valid (result, G_OBJECT (account),
1117           empathy_account_set_display_name_finish))
1118     return FALSE;
1119
1120   return TRUE;
1121 }
1122
1123 static void
1124 empathy_account_remove_cb (TpAccount *proxy,
1125     const GError *error,
1126     gpointer user_data,
1127     GObject *weak_object)
1128 {
1129   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
1130
1131   if (error != NULL)
1132     {
1133       g_simple_async_result_set_from_error (result, (GError *) error);
1134     }
1135
1136   g_simple_async_result_complete (result);
1137   g_object_unref (G_OBJECT (result));
1138 }
1139
1140 void
1141 empathy_account_remove_async (EmpathyAccount *account,
1142   GAsyncReadyCallback callback, gpointer user_data)
1143 {
1144   EmpathyAccountPriv *priv = GET_PRIV (account);
1145   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
1146       callback, user_data, empathy_account_remove_finish);
1147
1148   tp_cli_account_call_remove (priv->account,
1149       -1,
1150       empathy_account_remove_cb,
1151       result,
1152       NULL,
1153       G_OBJECT (account));
1154 }
1155
1156 gboolean
1157 empathy_account_remove_finish (EmpathyAccount *account,
1158   GAsyncResult *result, GError **error)
1159 {
1160   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1161       error))
1162     return FALSE;
1163
1164   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1165     G_OBJECT (account), empathy_account_update_settings_finish), FALSE);
1166
1167   return TRUE;
1168 }
1169