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