]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account.c
bb55abed7727fcd2c466c9ea33dfb473f949693f
[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
32 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
33 #include <libempathy/empathy-debug.h>
34
35 #include <glib/gi18n-lib.h>
36
37 #include "empathy-account.h"
38 #include "empathy-utils.h"
39 #include "empathy-marshal.h"
40
41 #define UNIQUE_NAME_PREFIX "/org/freedesktop/Telepathy/Account/"
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     TpConnection *connection);
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 (account, g_value_get_boolean (value));
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_string (value,
196             empathy_account_get_display_name (account));
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, GHashTable *properties)
206 {
207   EmpathyAccountPriv *priv = GET_PRIV (account);
208   const gchar *conn_path;
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     priv->display_name =
237       g_strdup (tp_asv_get_string (properties, "DisplayName"));
238
239   if (g_hash_table_lookup (properties, "Enabled") != NULL)
240     {
241       gboolean enabled = tp_asv_get_boolean (properties, "Enabled", NULL);
242       if (priv->enabled != enabled)
243         {
244           priv->enabled = enabled;
245           g_object_notify (G_OBJECT (account), "enabled");
246         }
247     }
248
249   if (g_hash_table_lookup (properties, "Valid") != NULL)
250     priv->valid = tp_asv_get_boolean (properties, "Valid", NULL);
251
252   if (g_hash_table_lookup (properties, "Parameters") != NULL)
253     {
254       GHashTable *parameters;
255
256       parameters = tp_asv_get_boxed (properties, "Parameters",
257         TP_HASH_TYPE_STRING_VARIANT_MAP);
258
259       priv->parameters = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP,
260         parameters);
261     }
262
263   if (!priv->ready)
264     {
265       priv->ready = TRUE;
266       g_object_notify (G_OBJECT (account), "ready");
267     }
268
269   if (priv->connection_status != old_s)
270     {
271       if (priv->connection_status == TP_CONNECTION_STATUS_CONNECTED)
272         {
273           GTimeVal val;
274           g_get_current_time (&val);
275
276           priv->connect_time = val.tv_sec;
277         }
278
279       g_signal_emit (account, signals[STATUS_CHANGED], 0,
280         old_s, priv->connection_status, priv->reason);
281
282       g_object_notify (G_OBJECT (account), "status");
283     }
284
285   if (presence_changed)
286     {
287       g_signal_emit (account, signals[PRESENCE_CHANGED], 0,
288         priv->presence, priv->status, priv->message);
289       g_object_notify (G_OBJECT (account), "presence");
290       g_object_notify (G_OBJECT (account), "status");
291       g_object_notify (G_OBJECT (account), "status-message");
292     }
293
294   if (g_hash_table_lookup (properties, "Connection") != NULL)
295     {
296       conn_path = tp_asv_get_object_path (properties, "Connection");
297
298       if (tp_strdiff (conn_path, "/") && priv->connection == NULL)
299         {
300           TpConnection *conn;
301           GError *error = NULL;
302           conn = tp_connection_new (priv->dbus, NULL, conn_path, &error);
303
304           if (conn == NULL)
305             {
306               DEBUG ("Failed to create a new TpConnection: %s",
307                 error->message);
308               g_error_free (error);
309             }
310
311           _empathy_account_set_connection (account, conn);
312         }
313     }
314 }
315
316 static void
317 empathy_account_properties_changed (TpAccount *proxy,
318     GHashTable *properties,
319     gpointer user_data,
320     GObject *weak_object)
321 {
322   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
323   EmpathyAccountPriv *priv = GET_PRIV (account);
324
325   if (!priv->ready)
326     return;
327
328   empathy_account_update (account, properties);
329 }
330
331 static void
332 empathy_account_removed_cb (TpAccount *proxy,
333     gpointer user_data,
334     GObject *weak_object)
335 {
336   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
337   EmpathyAccountPriv *priv = GET_PRIV (account);
338
339   if (priv->removed)
340     return;
341
342   priv->removed = TRUE;
343
344   g_signal_emit (account, signals[REMOVED], 0);
345 }
346
347 static void
348 empathy_account_got_all_cb (TpProxy *proxy,
349     GHashTable *properties,
350     const GError *error,
351     gpointer user_data,
352     GObject *weak_object)
353 {
354   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
355
356   DEBUG ("Got initial set of properties for %s",
357     empathy_account_get_unique_name (account));
358
359   if (error != NULL)
360     {
361       printf ("Unhappy\n");
362       return;
363     }
364
365   empathy_account_update (account, properties);
366 }
367
368 static gchar *
369 empathy_account_unescape_protocol (const gchar *protocol, gssize len)
370 {
371   gchar  *result, *escape;
372   /* Bad implementation might accidentally use tp_escape_as_identifier,
373    * which escapes - in the wrong way... */
374   if ((escape = g_strstr_len (protocol, len, "_2d")) != NULL)
375     {
376       GString *str;
377       const gchar *input;
378
379       str = g_string_new ("");
380       input = protocol;
381       do {
382         g_string_append_len (str, input, escape - input);
383         g_string_append_c (str, '-');
384
385         len -= escape - input + 3;
386         input = escape + 3;
387       } while ((escape = g_strstr_len (input, len, "_2d")) != NULL);
388
389       g_string_append_len (str, input, len);
390
391       result = g_string_free (str, FALSE);
392     }
393   else
394     {
395       result = g_strndup (protocol, len);
396     }
397
398   g_strdelimit (result, "_", '-');
399
400   return result;
401 }
402
403 static gboolean
404 empathy_account_parse_unique_name (const gchar *bus_name,
405   gchar **protocol, gchar **manager)
406 {
407   const gchar *proto, *proto_end;
408   const gchar *cm, *cm_end;
409
410   g_return_val_if_fail (
411     g_str_has_prefix (bus_name, UNIQUE_NAME_PREFIX), FALSE);
412
413   cm = bus_name + strlen (UNIQUE_NAME_PREFIX);
414
415   for (cm_end = cm; *cm_end != '/' && *cm_end != '\0'; cm_end++)
416     /* pass */;
417
418   if (*cm_end == '\0')
419     return FALSE;
420
421   if (cm_end == '\0')
422     return FALSE;
423
424   proto = cm_end + 1;
425
426   for (proto_end = proto; *proto_end != '/' && *proto_end != '\0'; proto_end++)
427     /* pass */;
428
429   if (*proto_end == '\0')
430     return FALSE;
431
432   if (protocol != NULL)
433     {
434       *protocol = empathy_account_unescape_protocol (proto, proto_end - proto);
435     }
436
437   if (manager != NULL)
438     *manager = g_strndup (cm, cm_end - cm);
439
440   return TRUE;
441 }
442
443 static void
444 account_invalidated_cb (TpProxy *proxy, guint domain, gint code,
445   gchar *message, gpointer user_data)
446 {
447   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
448   EmpathyAccountPriv *priv = GET_PRIV (account);
449
450   if (priv->removed)
451     return;
452
453   priv->removed = TRUE;
454
455   g_signal_emit (account, signals[REMOVED], 0);
456 }
457
458 static void
459 empathy_account_constructed (GObject *object)
460 {
461   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
462   EmpathyAccountPriv *priv = GET_PRIV (account);
463
464   priv->account = tp_account_new (priv->dbus, priv->unique_name, NULL);
465
466   g_signal_connect (priv->account, "invalidated",
467     G_CALLBACK (account_invalidated_cb), object);
468
469   empathy_account_parse_unique_name (priv->unique_name,
470     &(priv->proto_name), &(priv->cm_name));
471
472   priv->icon_name = g_strdup_printf ("im-%s", priv->proto_name);
473
474   tp_cli_account_connect_to_account_property_changed (priv->account,
475     empathy_account_properties_changed,
476     NULL, NULL, object, NULL);
477
478   tp_cli_account_connect_to_removed (priv->account,
479     empathy_account_removed_cb,
480     NULL, NULL, object, NULL);
481
482   tp_cli_dbus_properties_call_get_all (priv->account, -1,
483     TP_IFACE_ACCOUNT,
484     empathy_account_got_all_cb,
485     NULL,
486     NULL,
487     G_OBJECT (account));
488 }
489
490 static void empathy_account_dispose (GObject *object);
491 static void empathy_account_finalize (GObject *object);
492
493 static void
494 empathy_account_class_init (EmpathyAccountClass *empathy_account_class)
495 {
496   GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_class);
497
498   g_type_class_add_private (empathy_account_class,
499     sizeof (EmpathyAccountPriv));
500
501   object_class->set_property = empathy_account_set_property;
502   object_class->get_property = empathy_account_get_property;
503   object_class->dispose = empathy_account_dispose;
504   object_class->finalize = empathy_account_finalize;
505   object_class->constructed = empathy_account_constructed;
506
507   g_object_class_install_property (object_class, PROP_ENABLED,
508     g_param_spec_boolean ("enabled",
509       "Enabled",
510       "Whether this account is enabled or not",
511       FALSE,
512       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
513
514   g_object_class_install_property (object_class, PROP_READY,
515     g_param_spec_boolean ("ready",
516       "Ready",
517       "Whether this account is ready to be used",
518       FALSE,
519       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
520
521   g_object_class_install_property (object_class, PROP_PRESENCE,
522     g_param_spec_uint ("presence",
523       "Presence",
524       "The account connections presence type",
525       0,
526       NUM_TP_CONNECTION_PRESENCE_TYPES,
527       TP_CONNECTION_PRESENCE_TYPE_UNSET,
528       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
529
530   g_object_class_install_property (object_class, PROP_STATUS,
531     g_param_spec_string ("status",
532       "Status",
533       "The Status string of the account",
534       NULL,
535       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
536
537   g_object_class_install_property (object_class, PROP_STATUS_MESSAGE,
538     g_param_spec_string ("status-message",
539       "status-message",
540       "The Status message string of the account",
541       NULL,
542       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
543
544   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS,
545     g_param_spec_uint ("connection-status",
546       "ConnectionStatus",
547       "The accounts connections status type",
548       0,
549       NUM_TP_CONNECTION_STATUSES,
550       TP_CONNECTION_STATUS_DISCONNECTED,
551       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
552
553   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS_REASON,
554     g_param_spec_uint ("status-reason",
555       "ConnectionStatusReason",
556       "The account connections status reason",
557       0,
558       NUM_TP_CONNECTION_STATUS_REASONS,
559       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
560       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
561
562   g_object_class_install_property (object_class, PROP_CONNECTION,
563     g_param_spec_object ("connection",
564       "Connection",
565       "The accounts connection",
566       TP_TYPE_CONNECTION,
567       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
568
569   g_object_class_install_property (object_class, PROP_UNIQUE_NAME,
570     g_param_spec_string ("unique-name",
571       "UniqueName",
572       "The accounts unique name",
573       NULL,
574       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
575
576   g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
577     g_param_spec_object ("dbus-daemon",
578       "dbus-daemon",
579       "The Tp Dbus daemon on which this account exists",
580       TP_TYPE_DBUS_DAEMON,
581       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
582
583   g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
584     g_param_spec_string ("display-name",
585       "DisplayName",
586       "The accounts display name",
587       NULL,
588       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
589
590   signals[STATUS_CHANGED] = g_signal_new ("status-changed",
591     G_TYPE_FROM_CLASS (object_class),
592     G_SIGNAL_RUN_LAST,
593     0, NULL, NULL,
594     _empathy_marshal_VOID__UINT_UINT_UINT,
595     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
596
597   signals[PRESENCE_CHANGED] = g_signal_new ("presence-changed",
598     G_TYPE_FROM_CLASS (object_class),
599     G_SIGNAL_RUN_LAST,
600     0, NULL, NULL,
601     _empathy_marshal_VOID__UINT_STRING_STRING,
602     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
603
604   signals[REMOVED] = g_signal_new ("removed",
605     G_TYPE_FROM_CLASS (object_class),
606     G_SIGNAL_RUN_LAST,
607     0, NULL, NULL,
608     g_cclosure_marshal_VOID__VOID,
609     G_TYPE_NONE, 0);
610 }
611
612 void
613 empathy_account_dispose (GObject *object)
614 {
615   EmpathyAccount *self = EMPATHY_ACCOUNT (object);
616   EmpathyAccountPriv *priv = GET_PRIV (self);
617
618   if (priv->dispose_has_run)
619     return;
620
621   priv->dispose_has_run = TRUE;
622
623   if (priv->connection_invalidated_id != 0)
624     g_signal_handler_disconnect (priv->connection,
625         priv->connection_invalidated_id);
626   priv->connection_invalidated_id = 0;
627
628   if (priv->connection != NULL)
629     g_object_unref (priv->connection);
630   priv->connection = NULL;
631
632   /* release any references held by the object here */
633   if (G_OBJECT_CLASS (empathy_account_parent_class)->dispose != NULL)
634     G_OBJECT_CLASS (empathy_account_parent_class)->dispose (object);
635 }
636
637 void
638 empathy_account_finalize (GObject *object)
639 {
640   EmpathyAccountPriv *priv = GET_PRIV (object);
641
642   g_free (priv->status);
643   g_free (priv->message);
644
645   g_free (priv->cm_name);
646   g_free (priv->proto_name);
647   g_free (priv->icon_name);
648   g_free (priv->display_name);
649
650   /* free any data held directly by the object here */
651   if (G_OBJECT_CLASS (empathy_account_parent_class)->finalize != NULL)
652     G_OBJECT_CLASS (empathy_account_parent_class)->finalize (object);
653 }
654
655 gboolean
656 empathy_account_is_just_connected (EmpathyAccount *account)
657 {
658   EmpathyAccountPriv *priv = GET_PRIV (account);
659   GTimeVal val;
660
661   if (priv->connection_status != TP_CONNECTION_STATUS_CONNECTED)
662     return FALSE;
663
664   g_get_current_time (&val);
665
666   return (val.tv_sec - priv->connect_time) < 10;
667 }
668
669 /**
670  * empathy_account_get_connection:
671  * @account: a #EmpathyAccount
672  *
673  * Get the connection of the account, or NULL if account is offline or the
674  * connection is not yet ready. This function does not return a new ref.
675  *
676  * Returns: the connection of the account.
677  **/
678 TpConnection *
679 empathy_account_get_connection (EmpathyAccount *account)
680 {
681   EmpathyAccountPriv *priv = GET_PRIV (account);
682
683   if (priv->connection != NULL &&
684       tp_connection_is_ready (priv->connection))
685     return priv->connection;
686
687   return NULL;
688 }
689
690 /**
691  * empathy_account_get_unique_name:
692  * @account: a #EmpathyAccount
693  *
694  * Returns: the unique name of the account.
695  **/
696 const gchar *
697 empathy_account_get_unique_name (EmpathyAccount *account)
698 {
699   EmpathyAccountPriv *priv = GET_PRIV (account);
700
701   return priv->unique_name;
702 }
703
704 /**
705  * empathy_account_get_display_name:
706  * @account: a #EmpathyAccount
707  *
708  * Returns: the display name of the account.
709  **/
710 const gchar *
711 empathy_account_get_display_name (EmpathyAccount *account)
712 {
713   EmpathyAccountPriv *priv = GET_PRIV (account);
714
715   return priv->display_name;
716 }
717
718 gboolean
719 empathy_account_is_valid (EmpathyAccount *account)
720 {
721   EmpathyAccountPriv *priv = GET_PRIV (account);
722
723   return priv->valid;
724 }
725
726 const gchar *
727 empathy_account_get_connection_manager (EmpathyAccount *account)
728 {
729   EmpathyAccountPriv *priv = GET_PRIV (account);
730
731   return priv->cm_name;
732 }
733
734 const gchar *
735 empathy_account_get_protocol (EmpathyAccount *account)
736 {
737   EmpathyAccountPriv *priv = GET_PRIV (account);
738
739   return priv->proto_name;
740 }
741
742 const gchar *
743 empathy_account_get_icon_name (EmpathyAccount *account)
744 {
745   EmpathyAccountPriv *priv = GET_PRIV (account);
746
747   return priv->icon_name;
748 }
749
750 const GHashTable *
751 empathy_account_get_parameters (EmpathyAccount *account)
752 {
753   EmpathyAccountPriv *priv = GET_PRIV (account);
754
755   return priv->parameters;
756 }
757
758 gboolean
759 empathy_account_is_enabled (EmpathyAccount *account)
760 {
761   EmpathyAccountPriv *priv = GET_PRIV (account);
762
763   return priv->enabled;
764 }
765
766 gboolean
767 empathy_account_is_ready (EmpathyAccount *account)
768 {
769   EmpathyAccountPriv *priv = GET_PRIV (account);
770
771   return priv->ready;
772 }
773
774
775 EmpathyAccount *
776 empathy_account_new (TpDBusDaemon *dbus, const gchar *unique_name)
777 {
778   return EMPATHY_ACCOUNT (g_object_new (EMPATHY_TYPE_ACCOUNT,
779     "dbus-daemon", dbus,
780     "unique-name", unique_name,
781     NULL));
782 }
783
784 static void
785 empathy_account_connection_ready_cb (TpConnection *connection,
786     const GError *error,
787     gpointer user_data)
788 {
789   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
790   EmpathyAccountPriv *priv = GET_PRIV (account);
791
792   if (error != NULL)
793     {
794       DEBUG ("(%s) Connection failed to become ready: %s",
795         empathy_account_get_unique_name (account), error->message);
796       priv->connection = NULL;
797     }
798   else
799     {
800       DEBUG ("(%s) Connection ready",
801         empathy_account_get_unique_name (account));
802       g_object_notify (G_OBJECT (account), "connection");
803     }
804 }
805
806 static void
807 _empathy_account_connection_invalidated_cb (TpProxy *self,
808   guint    domain,
809   gint     code,
810   gchar   *message,
811   gpointer user_data)
812 {
813   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
814   EmpathyAccountPriv *priv = GET_PRIV (account);
815
816   if (priv->connection == NULL)
817     return;
818
819   DEBUG ("(%s) Connection invalidated",
820     empathy_account_get_unique_name (account));
821
822   g_assert (priv->connection == TP_CONNECTION (self));
823
824   g_signal_handler_disconnect (priv->connection,
825     priv->connection_invalidated_id);
826   priv->connection_invalidated_id = 0;
827
828   g_object_unref (priv->connection);
829   priv->connection = NULL;
830
831   g_object_notify (G_OBJECT (account), "connection");
832 }
833
834 static void
835 _empathy_account_set_connection (EmpathyAccount *account,
836     TpConnection *connection)
837 {
838   EmpathyAccountPriv *priv = GET_PRIV (account);
839
840   if (priv->connection == connection)
841     return;
842
843   /* Connection already set, don't set the new one */
844   if (connection != NULL && priv->connection != NULL)
845     return;
846
847   if (connection == NULL)
848     {
849       g_signal_handler_disconnect (priv->connection,
850         priv->connection_invalidated_id);
851       priv->connection_invalidated_id = 0;
852
853       g_object_unref (priv->connection);
854       priv->connection = NULL;
855       g_object_notify (G_OBJECT (account), "connection");
856     }
857   else
858     {
859       priv->connection = g_object_ref (connection);
860       priv->connection_invalidated_id = g_signal_connect (priv->connection,
861           "invalidated",
862           G_CALLBACK (_empathy_account_connection_invalidated_cb),
863           account);
864
865       DEBUG ("Readying connection for %s", priv->unique_name);
866       /* notify a change in the connection property when it's ready */
867       tp_connection_call_when_ready (priv->connection,
868         empathy_account_connection_ready_cb, account);
869     }
870 }
871
872 void
873 empathy_account_set_enabled (EmpathyAccount *account,
874     gboolean enabled)
875 {
876   EmpathyAccountPriv *priv = GET_PRIV (account);
877   GValue value = {0, };
878
879   if (priv->enabled == enabled)
880     return;
881
882   g_value_init (&value, G_TYPE_BOOLEAN);
883   g_value_set_boolean (&value, enabled);
884
885   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
886     -1,
887     TP_IFACE_ACCOUNT,
888     "Enabled",
889     &value,
890     NULL,
891     NULL,
892     NULL,
893     NULL);
894
895   g_value_unset (&value);
896 }
897
898 static void
899 empathy_account_requested_presence_cb (TpProxy *proxy,
900   const GError *error,
901   gpointer user_data,
902   GObject *weak_object)
903 {
904   if (error)
905     DEBUG (":( : %s", error->message);
906 }
907
908
909 void
910 empathy_account_request_presence (EmpathyAccount *account,
911   TpConnectionPresenceType type,
912   const gchar *status,
913   const gchar *message)
914 {
915   EmpathyAccountPriv *priv = GET_PRIV (account);
916   GValue value = {0, };
917   GValueArray *arr;
918
919   g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
920   g_value_take_boxed (&value, dbus_g_type_specialized_construct
921     (TP_STRUCT_TYPE_SIMPLE_PRESENCE));
922   arr = (GValueArray *) g_value_get_boxed (&value);
923
924   g_value_set_uint (arr->values, type);
925   g_value_set_static_string (arr->values + 1, status);
926   g_value_set_static_string (arr->values + 2, message);
927
928   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
929     -1,
930     TP_IFACE_ACCOUNT,
931     "RequestedPresence",
932     &value,
933     empathy_account_requested_presence_cb,
934     NULL,
935     NULL,
936     G_OBJECT (account));
937
938   g_value_unset (&value);
939 }
940
941 static void
942 empathy_account_updated_cb (TpAccount *proxy,
943     const gchar **reconnect_required,
944     const GError *error,
945     gpointer user_data,
946     GObject *weak_object)
947 {
948   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
949
950   if (error != NULL)
951     {
952       g_simple_async_result_set_from_error (result, (GError *) error);
953     }
954
955   g_simple_async_result_complete (result);
956   g_object_unref (G_OBJECT (result));
957 }
958
959 void
960 empathy_account_update_settings_async (EmpathyAccount *account,
961   GHashTable *parameters, const gchar **unset_parameters,
962   GAsyncReadyCallback callback, gpointer user_data)
963 {
964   EmpathyAccountPriv *priv = GET_PRIV (account);
965   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
966       callback, user_data, empathy_account_update_settings_finish);
967
968   tp_cli_account_call_update_parameters (priv->account,
969       -1,
970       parameters,
971       unset_parameters,
972       empathy_account_updated_cb,
973       result,
974       NULL,
975       G_OBJECT (account));
976 }
977
978 gboolean
979 empathy_account_update_settings_finish (EmpathyAccount *account,
980   GAsyncResult *result, GError **error)
981 {
982   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
983       error))
984     return FALSE;
985
986   g_return_val_if_fail (g_simple_async_result_is_valid (result,
987     G_OBJECT (account), empathy_account_update_settings_finish), FALSE);
988
989   return TRUE;
990 }
991
992 static void
993 account_display_name_set_cb (TpProxy *proxy,
994     const GError *error,
995     gpointer user_data,
996     GObject *weak_object)
997 {
998   GSimpleAsyncResult *result = user_data;
999
1000   if (error != NULL)
1001     g_simple_async_result_set_from_error (result, (GError *) error);
1002
1003   g_simple_async_result_complete (result);
1004   g_object_unref (result);
1005 }
1006
1007 void
1008 empathy_account_set_display_name_async (EmpathyAccount *account,
1009     const char *display_name,
1010     GAsyncReadyCallback callback,
1011     gpointer user_data)
1012 {
1013   GSimpleAsyncResult *result;
1014   GValue value = {0, };
1015   EmpathyAccountPriv *priv = GET_PRIV (account);
1016
1017   if (display_name == NULL)
1018     {
1019       g_simple_async_report_error_in_idle (G_OBJECT (account),
1020           callback, user_data, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1021           _("Can't set an empty display name"));
1022       return;
1023     }
1024
1025   result = g_simple_async_result_new (G_OBJECT (account), callback,
1026       user_data, empathy_account_set_display_name_finish);
1027
1028   g_value_init (&value, G_TYPE_STRING);
1029   g_value_set_string (&value, display_name);
1030
1031   tp_cli_dbus_properties_call_set (priv->account, -1, TP_IFACE_ACCOUNT,
1032       "DisplayName", &value, account_display_name_set_cb, result, NULL,
1033       G_OBJECT (account));
1034 }
1035
1036 gboolean
1037 empathy_account_set_display_name_finish (EmpathyAccount *account,
1038     GAsyncResult *result, GError **error)
1039 {
1040   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1041           error) ||
1042       !g_simple_async_result_is_valid (result, G_OBJECT (account),
1043           empathy_account_set_display_name_finish))
1044     return FALSE;
1045
1046   return TRUE;
1047 }
1048
1049 static void
1050 empathy_account_remove_cb (TpAccount *proxy,
1051     const GError *error,
1052     gpointer user_data,
1053     GObject *weak_object)
1054 {
1055   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
1056
1057   if (error != NULL)
1058     {
1059       g_simple_async_result_set_from_error (result, (GError *) error);
1060     }
1061
1062   g_simple_async_result_complete (result);
1063   g_object_unref (G_OBJECT (result));
1064 }
1065
1066 void
1067 empathy_account_remove_async (EmpathyAccount *account,
1068   GAsyncReadyCallback callback, gpointer user_data)
1069 {
1070   EmpathyAccountPriv *priv = GET_PRIV (account);
1071   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
1072       callback, user_data, empathy_account_remove_finish);
1073
1074   tp_cli_account_call_remove (priv->account,
1075       -1,
1076       empathy_account_remove_cb,
1077       result,
1078       NULL,
1079       G_OBJECT (account));
1080 }
1081
1082 gboolean
1083 empathy_account_remove_finish (EmpathyAccount *account,
1084   GAsyncResult *result, GError **error)
1085 {
1086   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1087       error))
1088     return FALSE;
1089
1090   g_return_val_if_fail (g_simple_async_result_is_valid (result,
1091     G_OBJECT (account), empathy_account_update_settings_finish), FALSE);
1092
1093   return TRUE;
1094 }
1095