]> git.0d.be Git - empathy.git/blob - libempathy/empathy-account.c
Coding style fixes
[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 "empathy-account.h"
36 #include "empathy-utils.h"
37 #include "empathy-marshal.h"
38
39 #define UNIQUE_NAME_PREFIX "/org/freedesktop/Telepathy/Account/"
40
41 /* signals */
42 enum {
43   STATUS_CHANGED,
44   PRESENCE_CHANGED,
45   LAST_SIGNAL
46 };
47
48 static guint signals[LAST_SIGNAL];
49
50 /* properties */
51 enum {
52   PROP_ENABLED = 1,
53   PROP_PRESENCE,
54   PROP_STATUS,
55   PROP_STATUS_MESSAGE,
56   PROP_READY,
57   PROP_CONNECTION_STATUS,
58   PROP_CONNECTION_STATUS_REASON,
59   PROP_CONNECTION,
60   PROP_UNIQUE_NAME,
61   PROP_DBUS_DAEMON,
62   PROP_DISPLAY_NAME
63 };
64
65 G_DEFINE_TYPE(EmpathyAccount, empathy_account, G_TYPE_OBJECT)
66
67 /* private structure */
68 typedef struct _EmpathyAccountPriv EmpathyAccountPriv;
69
70 struct _EmpathyAccountPriv
71 {
72   gboolean dispose_has_run;
73
74   TpConnection *connection;
75   guint connection_invalidated_id;
76
77   TpConnectionStatus connection_status;
78   TpConnectionStatusReason reason;
79
80   TpConnectionPresenceType presence;
81   gchar *status;
82   gchar *message;
83
84   gboolean enabled;
85   gboolean valid;
86   gboolean ready;
87   /* Timestamp when the connection got connected in seconds since the epoch */
88   glong connect_time;
89
90   gchar *cm_name;
91   gchar *proto_name;
92   gchar *icon_name;
93
94   gchar *unique_name;
95   gchar *display_name;
96   TpDBusDaemon *dbus;
97
98   TpAccount *account;
99   GHashTable *parameters;
100 };
101
102 #define GET_PRIV(obj)  EMPATHY_GET_PRIV (obj, EmpathyAccount)
103
104 static void _empathy_account_set_connection (EmpathyAccount *account,
105     TpConnection *connection);
106
107 static void
108 empathy_account_init (EmpathyAccount *obj)
109 {
110   EmpathyAccountPriv *priv;
111
112   priv =  G_TYPE_INSTANCE_GET_PRIVATE (obj,
113     EMPATHY_TYPE_ACCOUNT, EmpathyAccountPriv);
114
115   obj->priv = priv;
116
117   priv->connection_status = TP_CONNECTION_STATUS_DISCONNECTED;
118 }
119
120 static void
121 empathy_account_set_property (GObject *object,
122     guint prop_id,
123     const GValue *value,
124     GParamSpec *pspec)
125 {
126   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
127   EmpathyAccountPriv *priv = GET_PRIV (account);
128
129   switch (prop_id)
130     {
131       case PROP_ENABLED:
132         empathy_account_set_enabled (account, g_value_get_boolean (value));
133         break;
134       case PROP_UNIQUE_NAME:
135         priv->unique_name = g_value_dup_string (value);
136         break;
137       case PROP_DBUS_DAEMON:
138         priv->dbus = g_value_get_object (value);
139         break;
140       default:
141         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
142         break;
143     }
144 }
145
146 static void
147 empathy_account_get_property (GObject *object,
148     guint prop_id,
149     GValue *value,
150     GParamSpec *pspec)
151 {
152   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
153   EmpathyAccountPriv *priv = GET_PRIV (account);
154
155   switch (prop_id)
156     {
157       case PROP_ENABLED:
158         g_value_set_boolean (value, priv->enabled);
159         break;
160       case PROP_READY:
161         g_value_set_boolean (value, priv->ready);
162         break;
163       case PROP_PRESENCE:
164         g_value_set_uint (value, priv->presence);
165         break;
166       case PROP_STATUS:
167         g_value_set_string (value, priv->status);
168         break;
169       case PROP_STATUS_MESSAGE:
170         g_value_set_string (value, priv->message);
171         break;
172       case PROP_CONNECTION_STATUS:
173         g_value_set_uint (value, priv->connection_status);
174         break;
175       case PROP_CONNECTION_STATUS_REASON:
176         g_value_set_uint (value, priv->reason);
177         break;
178       case PROP_CONNECTION:
179         g_value_set_object (value,
180             empathy_account_get_connection (account));
181         break;
182       case PROP_UNIQUE_NAME:
183         g_value_set_string (value,
184             empathy_account_get_unique_name (account));
185         break;
186       case PROP_DISPLAY_NAME:
187         g_value_set_string (value,
188             empathy_account_get_display_name (account));
189         break;
190       case PROP_DBUS_DAEMON:
191         g_value_set_string (value,
192             empathy_account_get_display_name (account));
193         break;
194       default:
195         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196         break;
197     }
198 }
199
200 static void
201 empathy_account_update (EmpathyAccount *account, GHashTable *properties)
202 {
203   EmpathyAccountPriv *priv = GET_PRIV (account);
204   const gchar *conn_path;
205   GValueArray *arr;
206   TpConnectionStatus old_s = priv->connection_status;
207   gboolean presence_changed = FALSE;
208
209   if (g_hash_table_lookup (properties, "ConnectionStatus") != NULL)
210     priv->connection_status =
211       tp_asv_get_int32 (properties, "ConnectionStatus", NULL);
212
213   if (g_hash_table_lookup (properties, "ConnectionStatusReason") != NULL)
214     priv->reason = tp_asv_get_int32 (properties,
215       "ConnectionStatusReason", NULL);
216
217   if (g_hash_table_lookup (properties, "CurrentPresence") != NULL)
218     {
219       presence_changed = TRUE;
220       arr = tp_asv_get_boxed (properties, "CurrentPresence",
221         TP_STRUCT_TYPE_SIMPLE_PRESENCE);
222       priv->presence = g_value_get_uint (g_value_array_get_nth (arr, 0));
223
224       g_free (priv->status);
225       priv->status = g_value_dup_string (g_value_array_get_nth (arr, 1));
226
227       g_free (priv->message);
228       priv->message = g_value_dup_string (g_value_array_get_nth (arr, 2));
229     }
230
231   if (g_hash_table_lookup (properties, "DisplayName") != NULL)
232     priv->display_name =
233       g_strdup (tp_asv_get_string (properties, "DisplayName"));
234
235   if (g_hash_table_lookup (properties, "Enabled") != NULL)
236     {
237       gboolean enabled = tp_asv_get_boolean (properties, "Enabled", NULL);
238       if (priv->enabled != enabled)
239         {
240           priv->enabled = enabled;
241           g_object_notify (G_OBJECT (account), "enabled");
242         }
243     }
244
245   if (g_hash_table_lookup (properties, "Valid") != NULL)
246     priv->valid = tp_asv_get_boolean (properties, "Valid", NULL);
247
248   if (g_hash_table_lookup (properties, "Parameters") != NULL)
249     {
250       GHashTable *parameters;
251
252       parameters = tp_asv_get_boxed (properties, "Parameters",
253         TP_HASH_TYPE_STRING_VARIANT_MAP);
254
255       priv->parameters = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP,
256         parameters);
257     }
258
259   if (!priv->ready)
260     {
261       priv->ready = TRUE;
262       g_object_notify (G_OBJECT (account), "ready");
263     }
264
265   if (priv->connection_status != old_s)
266     {
267       if (priv->connection_status == TP_CONNECTION_STATUS_CONNECTED)
268         {
269           GTimeVal val;
270           g_get_current_time (&val);
271
272           priv->connect_time = val.tv_sec;
273         }
274
275       g_signal_emit (account, signals[STATUS_CHANGED], 0,
276         old_s, priv->connection_status, priv->reason);
277
278       g_object_notify (G_OBJECT (account), "status");
279     }
280
281   if (presence_changed)
282     {
283       g_signal_emit (account, signals[PRESENCE_CHANGED], 0,
284         priv->presence, priv->status, priv->message);
285       g_object_notify (G_OBJECT (account), "presence");
286       g_object_notify (G_OBJECT (account), "status");
287       g_object_notify (G_OBJECT (account), "status-message");
288     }
289
290   if (g_hash_table_lookup (properties, "Connection") != NULL)
291     {
292       conn_path = tp_asv_get_object_path (properties, "Connection");
293
294       if (tp_strdiff (conn_path, "/") && priv->connection == NULL)
295         {
296           TpConnection *conn;
297           GError *error = NULL;
298           conn = tp_connection_new (priv->dbus, NULL, conn_path, &error);
299
300           if (conn == NULL)
301             {
302               DEBUG ("Failed to create a new TpConnection: %s",
303                 error->message);
304               g_error_free (error);
305             }
306
307           _empathy_account_set_connection (account, conn);
308         }
309     }
310 }
311
312 static void
313 empathy_account_properties_changed (TpAccount *proxy,
314     GHashTable *properties,
315     gpointer user_data,
316     GObject *weak_object)
317 {
318   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
319   EmpathyAccountPriv *priv = GET_PRIV (account);
320
321   if (!priv->ready)
322     return;
323
324   empathy_account_update (account, properties);
325 }
326
327 static void
328 empathy_account_got_all_cb (TpProxy *proxy,
329     GHashTable *properties,
330     const GError *error,
331     gpointer user_data,
332     GObject *weak_object)
333 {
334   EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
335
336   DEBUG ("Got initial set of properties for %s",
337     empathy_account_get_unique_name (account));
338
339   if (error != NULL)
340     {
341       printf ("Unhappy\n");
342       return;
343     }
344
345   empathy_account_update (account, properties);
346 }
347
348 static gchar *
349 empathy_account_unescape_protocol (const gchar *protocol, gssize len)
350 {
351   gchar  *result, *escape;
352   /* Bad implementation might accidentally use tp_escape_as_identifier,
353    * which escapes - in the wrong way... */
354   if ((escape = g_strstr_len (protocol, len, "_2d")) != NULL)
355     {
356       GString *str;
357       const gchar *input;
358
359       str = g_string_new ("");
360       input = protocol;
361       do {
362         g_string_append_len (str, input, escape - input);
363         g_string_append_c (str, '-');
364
365         len -= escape - input + 3;
366         input = escape + 3;
367       } while ((escape = g_strstr_len (input, len, "_2d")) != NULL);
368
369       g_string_append_len (str, input, len);
370
371       result = g_string_free (str, FALSE);
372     }
373   else
374     {
375       result = g_strndup (protocol, len);
376     }
377
378   g_strdelimit (result, "_", '-');
379
380   return result;
381 }
382
383 static gboolean
384 empathy_account_parse_unique_name (const gchar *bus_name,
385   gchar **protocol, gchar **manager)
386 {
387   const gchar *proto, *proto_end;
388   const gchar *cm, *cm_end;
389
390   g_return_val_if_fail (
391     g_str_has_prefix (bus_name, UNIQUE_NAME_PREFIX), FALSE);
392
393   cm = bus_name + strlen (UNIQUE_NAME_PREFIX);
394
395   for (cm_end = cm; *cm_end != '/' && *cm_end != '\0'; cm_end++)
396     /* pass */;
397
398   if (*cm_end == '\0')
399     return FALSE;
400
401   if (cm_end == '\0')
402     return FALSE;
403
404   proto = cm_end + 1;
405
406   for (proto_end = proto; *proto_end != '/' && *proto_end != '\0'; proto_end++)
407     /* pass */;
408
409   if (*proto_end == '\0')
410     return FALSE;
411
412   if (protocol != NULL)
413     {
414       *protocol = empathy_account_unescape_protocol (proto, proto_end - proto);
415     }
416
417   if (manager != NULL)
418     *manager = g_strndup (cm, cm_end - cm);
419
420   return TRUE;
421 }
422
423 static void
424 empathy_account_constructed (GObject *object)
425 {
426   EmpathyAccount *account = EMPATHY_ACCOUNT (object);
427   EmpathyAccountPriv *priv = GET_PRIV (account);
428
429   priv->account = tp_account_new (priv->dbus, priv->unique_name, NULL);
430
431   empathy_account_parse_unique_name (priv->unique_name,
432     &(priv->proto_name), &(priv->cm_name));
433
434   priv->icon_name = g_strdup_printf ("im-%s", priv->proto_name);
435
436   tp_cli_account_connect_to_account_property_changed (priv->account,
437     empathy_account_properties_changed,
438     NULL, NULL, object, NULL);
439
440   tp_cli_dbus_properties_call_get_all (priv->account, -1,
441     TP_IFACE_ACCOUNT,
442     empathy_account_got_all_cb,
443     NULL,
444     NULL,
445     G_OBJECT (account));
446 }
447
448 static void empathy_account_dispose (GObject *object);
449 static void empathy_account_finalize (GObject *object);
450
451 static void
452 empathy_account_class_init (EmpathyAccountClass *empathy_account_class)
453 {
454   GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_class);
455
456   g_type_class_add_private (empathy_account_class,
457     sizeof (EmpathyAccountPriv));
458
459   object_class->set_property = empathy_account_set_property;
460   object_class->get_property = empathy_account_get_property;
461   object_class->dispose = empathy_account_dispose;
462   object_class->finalize = empathy_account_finalize;
463   object_class->constructed = empathy_account_constructed;
464
465   g_object_class_install_property (object_class, PROP_ENABLED,
466     g_param_spec_boolean ("enabled",
467       "Enabled",
468       "Whether this account is enabled or not",
469       FALSE,
470       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
471
472   g_object_class_install_property (object_class, PROP_READY,
473     g_param_spec_boolean ("ready",
474       "Ready",
475       "Whether this account is ready to be used",
476       FALSE,
477       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
478
479   g_object_class_install_property (object_class, PROP_PRESENCE,
480     g_param_spec_uint ("presence",
481       "Presence",
482       "The account connections presence type",
483       0,
484       NUM_TP_CONNECTION_PRESENCE_TYPES,
485       TP_CONNECTION_PRESENCE_TYPE_UNSET,
486       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
487
488   g_object_class_install_property (object_class, PROP_STATUS,
489     g_param_spec_string ("status",
490       "Status",
491       "The Status string of the account",
492       NULL,
493       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
494
495   g_object_class_install_property (object_class, PROP_STATUS_MESSAGE,
496     g_param_spec_string ("status-message",
497       "status-message",
498       "The Status message string of the account",
499       NULL,
500       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
501
502   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS,
503     g_param_spec_uint ("connection-status",
504       "ConnectionStatus",
505       "The accounts connections status type",
506       0,
507       NUM_TP_CONNECTION_STATUSES,
508       TP_CONNECTION_STATUS_DISCONNECTED,
509       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
510
511   g_object_class_install_property (object_class, PROP_CONNECTION_STATUS_REASON,
512     g_param_spec_uint ("status-reason",
513       "ConnectionStatusReason",
514       "The account connections status reason",
515       0,
516       NUM_TP_CONNECTION_STATUS_REASONS,
517       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
518       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
519
520   g_object_class_install_property (object_class, PROP_CONNECTION,
521     g_param_spec_object ("connection",
522       "Connection",
523       "The accounts connection",
524       TP_TYPE_CONNECTION,
525       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
526
527   g_object_class_install_property (object_class, PROP_UNIQUE_NAME,
528     g_param_spec_string ("unique-name",
529       "UniqueName",
530       "The accounts unique name",
531       NULL,
532       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
533
534   g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
535     g_param_spec_object ("dbus-daemon",
536       "dbus-daemon",
537       "The Tp Dbus daemon on which this account exists",
538       TP_TYPE_DBUS_DAEMON,
539       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
540
541   g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
542     g_param_spec_string ("display-name",
543       "DisplayName",
544       "The accounts display name",
545       NULL,
546       G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
547
548   signals[STATUS_CHANGED] = g_signal_new ("status-changed",
549     G_TYPE_FROM_CLASS (object_class),
550     G_SIGNAL_RUN_LAST,
551     0, NULL, NULL,
552     _empathy_marshal_VOID__UINT_UINT_UINT,
553     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
554
555   signals[PRESENCE_CHANGED] = g_signal_new ("presence-changed",
556     G_TYPE_FROM_CLASS (object_class),
557     G_SIGNAL_RUN_LAST,
558     0, NULL, NULL,
559     _empathy_marshal_VOID__UINT_STRING_STRING,
560     G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
561 }
562
563 void
564 empathy_account_dispose (GObject *object)
565 {
566   EmpathyAccount *self = EMPATHY_ACCOUNT (object);
567   EmpathyAccountPriv *priv = GET_PRIV (self);
568
569   if (priv->dispose_has_run)
570     return;
571
572   priv->dispose_has_run = TRUE;
573
574   if (priv->connection_invalidated_id != 0)
575     g_signal_handler_disconnect (priv->connection,
576         priv->connection_invalidated_id);
577   priv->connection_invalidated_id = 0;
578
579   if (priv->connection != NULL)
580     g_object_unref (priv->connection);
581   priv->connection = NULL;
582
583   /* release any references held by the object here */
584   if (G_OBJECT_CLASS (empathy_account_parent_class)->dispose != NULL)
585     G_OBJECT_CLASS (empathy_account_parent_class)->dispose (object);
586 }
587
588 void
589 empathy_account_finalize (GObject *object)
590 {
591   EmpathyAccountPriv *priv = GET_PRIV (object);
592
593   g_free (priv->status);
594   g_free (priv->message);
595
596   g_free (priv->cm_name);
597   g_free (priv->proto_name);
598   g_free (priv->icon_name);
599   g_free (priv->display_name);
600
601   /* free any data held directly by the object here */
602   if (G_OBJECT_CLASS (empathy_account_parent_class)->finalize != NULL)
603     G_OBJECT_CLASS (empathy_account_parent_class)->finalize (object);
604 }
605
606 gboolean
607 empathy_account_is_just_connected (EmpathyAccount *account)
608 {
609   EmpathyAccountPriv *priv = GET_PRIV (account);
610   GTimeVal val;
611
612   if (priv->connection_status != TP_CONNECTION_STATUS_CONNECTED)
613     return FALSE;
614
615   g_get_current_time (&val);
616
617   return (val.tv_sec - priv->connect_time) < 10;
618 }
619
620 /**
621  * empathy_account_get_connection:
622  * @account: a #EmpathyAccount
623  *
624  * Get the connection of the account, or NULL if account is offline or the
625  * connection is not yet ready. This function does not return a new ref.
626  *
627  * Returns: the connection of the account.
628  **/
629 TpConnection *
630 empathy_account_get_connection (EmpathyAccount *account)
631 {
632   EmpathyAccountPriv *priv = GET_PRIV (account);
633
634   if (priv->connection != NULL &&
635       tp_connection_is_ready (priv->connection))
636     return priv->connection;
637
638   return NULL;
639 }
640
641 /**
642  * empathy_account_get_unique_name:
643  * @account: a #EmpathyAccount
644  *
645  * Returns: the unique name of the account.
646  **/
647 const gchar *
648 empathy_account_get_unique_name (EmpathyAccount *account)
649 {
650   EmpathyAccountPriv *priv = GET_PRIV (account);
651
652   return priv->unique_name;
653 }
654
655 /**
656  * empathy_account_get_display_name:
657  * @account: a #EmpathyAccount
658  *
659  * Returns: the display name of the account.
660  **/
661 const gchar *
662 empathy_account_get_display_name (EmpathyAccount *account)
663 {
664   EmpathyAccountPriv *priv = GET_PRIV (account);
665
666   return priv->display_name;
667 }
668
669 gboolean
670 empathy_account_is_valid (EmpathyAccount *account)
671 {
672   //EmpathyAccountPriv *priv = GET_PRIV (account);
673
674   /* FIXME */
675   return FALSE;
676 }
677
678 const gchar *
679 empathy_account_get_connection_manager (EmpathyAccount *account)
680 {
681   EmpathyAccountPriv *priv = GET_PRIV (account);
682
683   return priv->cm_name;
684 }
685
686 const gchar *
687 empathy_account_get_protocol (EmpathyAccount *account)
688 {
689   EmpathyAccountPriv *priv = GET_PRIV (account);
690
691   return priv->proto_name;
692 }
693
694 const gchar *
695 empathy_account_get_icon_name (EmpathyAccount *account)
696 {
697   EmpathyAccountPriv *priv = GET_PRIV (account);
698
699   return priv->icon_name;
700 }
701
702 const GHashTable *
703 empathy_account_get_parameters (EmpathyAccount *account)
704 {
705   EmpathyAccountPriv *priv = GET_PRIV (account);
706
707   return priv->parameters;
708 }
709
710 gboolean
711 empathy_account_is_enabled (EmpathyAccount *account)
712 {
713   EmpathyAccountPriv *priv = GET_PRIV (account);
714
715   return priv->enabled;
716 }
717
718 gboolean
719 empathy_account_is_ready (EmpathyAccount *account)
720 {
721   EmpathyAccountPriv *priv = GET_PRIV (account);
722
723   return priv->ready;
724 }
725
726
727 EmpathyAccount *
728 empathy_account_new (TpDBusDaemon *dbus, const gchar *unique_name)
729 {
730   return EMPATHY_ACCOUNT (g_object_new (EMPATHY_TYPE_ACCOUNT,
731     "dbus-daemon", dbus,
732     "unique-name", unique_name,
733     NULL));
734 }
735
736 static void
737 empathy_account_connection_ready_cb (TpConnection *connection,
738     const GError *error,
739     gpointer user_data)
740 {
741   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
742   EmpathyAccountPriv *priv = GET_PRIV (account);
743
744   if (error != NULL)
745     {
746       DEBUG ("(%s) Connection failed to become ready: %s",
747         empathy_account_get_unique_name (account), error->message);
748       priv->connection = NULL;
749     }
750   else
751     {
752       DEBUG ("(%s) Connection ready",
753         empathy_account_get_unique_name (account));
754       g_object_notify (G_OBJECT (account), "connection");
755     }
756 }
757
758 static void
759 _empathy_account_connection_invalidated_cb (TpProxy *self,
760   guint    domain,
761   gint     code,
762   gchar   *message,
763   gpointer user_data)
764 {
765   EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
766   EmpathyAccountPriv *priv = GET_PRIV (account);
767
768   if (priv->connection == NULL)
769     return;
770
771   DEBUG ("(%s) Connection invalidated",
772     empathy_account_get_unique_name (account));
773
774   g_assert (priv->connection == TP_CONNECTION (self));
775
776   g_signal_handler_disconnect (priv->connection,
777     priv->connection_invalidated_id);
778   priv->connection_invalidated_id = 0;
779
780   g_object_unref (priv->connection);
781   priv->connection = NULL;
782
783   g_object_notify (G_OBJECT (account), "connection");
784 }
785
786 static void
787 _empathy_account_set_connection (EmpathyAccount *account,
788     TpConnection *connection)
789 {
790   EmpathyAccountPriv *priv = GET_PRIV (account);
791
792   if (priv->connection == connection)
793     return;
794
795   /* Connection already set, don't set the new one */
796   if (connection != NULL && priv->connection != NULL)
797     return;
798
799   if (connection == NULL)
800     {
801       g_signal_handler_disconnect (priv->connection,
802         priv->connection_invalidated_id);
803       priv->connection_invalidated_id = 0;
804
805       g_object_unref (priv->connection);
806       priv->connection = NULL;
807       g_object_notify (G_OBJECT (account), "connection");
808     }
809   else
810     {
811       priv->connection = g_object_ref (connection);
812       priv->connection_invalidated_id = g_signal_connect (priv->connection,
813           "invalidated",
814           G_CALLBACK (_empathy_account_connection_invalidated_cb),
815           account);
816
817       DEBUG ("Readying connection for %s", priv->unique_name);
818       /* notify a change in the connection property when it's ready */
819       tp_connection_call_when_ready (priv->connection,
820         empathy_account_connection_ready_cb, account);
821     }
822 }
823
824 void
825 empathy_account_set_enabled (EmpathyAccount *account,
826     gboolean enabled)
827 {
828   EmpathyAccountPriv *priv = GET_PRIV (account);
829   GValue value = {0, };
830
831   if (priv->enabled == enabled)
832     return;
833
834   g_value_init (&value, G_TYPE_BOOLEAN);
835   g_value_set_boolean (&value, enabled);
836
837   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
838     -1,
839     TP_IFACE_ACCOUNT,
840     "Enabled",
841     &value,
842     NULL,
843     NULL,
844     NULL,
845     NULL);
846
847   g_value_unset (&value);
848 }
849
850 static void
851 empathy_account_requested_presence_cb (TpProxy *proxy,
852   const GError *error,
853   gpointer user_data,
854   GObject *weak_object)
855 {
856   if (error)
857     DEBUG (":( : %s", error->message);
858 }
859
860
861 void
862 empathy_account_request_presence (EmpathyAccount *account,
863   TpConnectionPresenceType type,
864   const gchar *status,
865   const gchar *message)
866 {
867   EmpathyAccountPriv *priv = GET_PRIV (account);
868   GValue value = {0, };
869   GValueArray *arr;
870
871   g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
872   g_value_take_boxed (&value, dbus_g_type_specialized_construct
873     (TP_STRUCT_TYPE_SIMPLE_PRESENCE));
874   arr = (GValueArray *) g_value_get_boxed (&value);
875
876   g_value_set_uint (arr->values, type);
877   g_value_set_static_string (arr->values + 1, status);
878   g_value_set_static_string (arr->values + 2, message);
879
880   tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
881     -1,
882     TP_IFACE_ACCOUNT,
883     "RequestedPresence",
884     &value,
885     empathy_account_requested_presence_cb,
886     NULL,
887     NULL,
888     G_OBJECT (account));
889
890   g_value_unset (&value);
891 }
892
893 static void
894 empathy_account_updated_cb (TpAccount *proxy,
895     const gchar **reconnect_required,
896     const GError *error,
897     gpointer user_data,
898     GObject *weak_object)
899 {
900   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
901
902   if (error != NULL)
903     {
904       g_simple_async_result_set_from_error (result, (GError *) error);
905     }
906
907   g_simple_async_result_complete (result);
908   g_object_unref (G_OBJECT (result));
909 }
910
911 void
912 empathy_account_update_settings_async (EmpathyAccount *account,
913   GHashTable *parameters, const gchar **unset_parameters,
914   GAsyncReadyCallback callback, gpointer user_data)
915 {
916   EmpathyAccountPriv *priv = GET_PRIV (account);
917   GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (account),
918       callback, user_data, empathy_account_update_settings_finish);
919
920   tp_cli_account_call_update_parameters (priv->account,
921       -1,
922       parameters,
923       unset_parameters,
924       empathy_account_updated_cb,
925       result,
926       NULL,
927       G_OBJECT (account));
928 }
929
930 gboolean
931 empathy_account_update_settings_finish (EmpathyAccount *account,
932   GAsyncResult *result, GError **error)
933 {
934   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
935       error))
936     return FALSE;
937
938   g_return_val_if_fail (g_simple_async_result_is_valid (result,
939     G_OBJECT (account), empathy_account_update_settings_finish), FALSE);
940
941   return TRUE;
942 }