2 * empathy-account.c - Source for EmpathyAccount
3 * Copyright (C) 2009 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
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.
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.
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
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>
32 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
33 #include <libempathy/empathy-debug.h>
35 #include "empathy-account.h"
36 #include "empathy-utils.h"
37 #include "empathy-marshal.h"
39 #define UNIQUE_NAME_PREFIX "/org/freedesktop/Telepathy/Account/"
48 static guint signals[LAST_SIGNAL];
57 PROP_CONNECTION_STATUS,
58 PROP_CONNECTION_STATUS_REASON,
65 G_DEFINE_TYPE(EmpathyAccount, empathy_account, G_TYPE_OBJECT)
67 /* private structure */
68 typedef struct _EmpathyAccountPriv EmpathyAccountPriv;
70 struct _EmpathyAccountPriv
72 gboolean dispose_has_run;
74 TpConnection *connection;
75 guint connection_invalidated_id;
77 TpConnectionStatus connection_status;
78 TpConnectionStatusReason reason;
80 TpConnectionPresenceType presence;
87 /* Timestamp when the connection got connected in seconds since the epoch */
99 GHashTable *parameters;
102 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccount)
104 static void _empathy_account_set_connection (EmpathyAccount *account,
105 TpConnection *connection);
108 empathy_account_init (EmpathyAccount *obj)
110 EmpathyAccountPriv *priv;
112 priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
113 EMPATHY_TYPE_ACCOUNT, EmpathyAccountPriv);
117 priv->connection_status = TP_CONNECTION_STATUS_DISCONNECTED;
121 empathy_account_set_property (GObject *object,
126 EmpathyAccount *account = EMPATHY_ACCOUNT (object);
127 EmpathyAccountPriv *priv = GET_PRIV (account);
132 empathy_account_set_enabled (account, g_value_get_boolean (value));
134 case PROP_UNIQUE_NAME:
135 priv->unique_name = g_value_dup_string (value);
137 case PROP_DBUS_DAEMON:
138 priv->dbus = g_value_get_object (value);
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147 empathy_account_get_property (GObject *object,
152 EmpathyAccount *account = EMPATHY_ACCOUNT (object);
153 EmpathyAccountPriv *priv = GET_PRIV (account);
158 g_value_set_boolean (value, priv->enabled);
161 g_value_set_boolean (value, priv->ready);
164 g_value_set_uint (value, priv->presence);
167 g_value_set_string (value, priv->status);
169 case PROP_STATUS_MESSAGE:
170 g_value_set_string (value, priv->message);
172 case PROP_CONNECTION_STATUS:
173 g_value_set_uint (value, priv->connection_status);
175 case PROP_CONNECTION_STATUS_REASON:
176 g_value_set_uint (value, priv->reason);
178 case PROP_CONNECTION:
179 g_value_set_object (value,
180 empathy_account_get_connection (account));
182 case PROP_UNIQUE_NAME:
183 g_value_set_string (value,
184 empathy_account_get_unique_name (account));
186 case PROP_DISPLAY_NAME:
187 g_value_set_string (value,
188 empathy_account_get_display_name (account));
190 case PROP_DBUS_DAEMON:
191 g_value_set_string (value,
192 empathy_account_get_display_name (account));
195 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
201 empathy_account_update (EmpathyAccount *account, GHashTable *properties)
203 EmpathyAccountPriv *priv = GET_PRIV (account);
204 const gchar *conn_path;
206 TpConnectionStatus old_s = priv->connection_status;
207 gboolean presence_changed = FALSE;
209 if (g_hash_table_lookup (properties, "ConnectionStatus") != NULL)
210 priv->connection_status =
211 tp_asv_get_int32 (properties, "ConnectionStatus", NULL);
213 if (g_hash_table_lookup (properties, "ConnectionStatusReason") != NULL)
214 priv->reason = tp_asv_get_int32 (properties,
215 "ConnectionStatusReason", NULL);
217 if (g_hash_table_lookup (properties, "CurrentPresence") != NULL)
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));
224 g_free (priv->status);
225 priv->status = g_value_dup_string (g_value_array_get_nth (arr, 1));
227 g_free (priv->message);
228 priv->message = g_value_dup_string (g_value_array_get_nth (arr, 2));
231 if (g_hash_table_lookup (properties, "DisplayName") != NULL)
233 g_strdup (tp_asv_get_string (properties, "DisplayName"));
235 if (g_hash_table_lookup (properties, "Enabled") != NULL)
237 gboolean enabled = tp_asv_get_boolean (properties, "Enabled", NULL);
238 if (priv->enabled != enabled)
240 priv->enabled = enabled;
241 g_object_notify (G_OBJECT (account), "enabled");
245 if (g_hash_table_lookup (properties, "Valid") != NULL)
246 priv->valid = tp_asv_get_boolean (properties, "Valid", NULL);
248 if (g_hash_table_lookup (properties, "Parameters") != NULL)
250 GHashTable *parameters;
252 parameters = tp_asv_get_boxed (properties, "Parameters",
253 TP_HASH_TYPE_STRING_VARIANT_MAP);
255 priv->parameters = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP,
262 g_object_notify (G_OBJECT (account), "ready");
265 if (priv->connection_status != old_s)
267 if (priv->connection_status == TP_CONNECTION_STATUS_CONNECTED)
270 g_get_current_time (&val);
272 priv->connect_time = val.tv_sec;
275 g_signal_emit (account, signals[STATUS_CHANGED], 0,
276 old_s, priv->connection_status, priv->reason);
278 g_object_notify (G_OBJECT (account), "status");
281 if (presence_changed)
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");
290 if (g_hash_table_lookup (properties, "Connection") != NULL)
292 conn_path = tp_asv_get_object_path (properties, "Connection");
294 if (tp_strdiff (conn_path, "/") && priv->connection == NULL)
297 GError *error = NULL;
298 conn = tp_connection_new (priv->dbus, NULL, conn_path, &error);
302 DEBUG ("Failed to create a new TpConnection: %s",
304 g_error_free (error);
307 _empathy_account_set_connection (account, conn);
313 empathy_account_properties_changed (TpAccount *proxy,
314 GHashTable *properties,
316 GObject *weak_object)
318 EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
319 EmpathyAccountPriv *priv = GET_PRIV (account);
324 empathy_account_update (account, properties);
328 empathy_account_got_all_cb (TpProxy *proxy,
329 GHashTable *properties,
332 GObject *weak_object)
334 EmpathyAccount *account = EMPATHY_ACCOUNT (weak_object);
336 DEBUG ("Got initial set of properties for %s",
337 empathy_account_get_unique_name (account));
341 printf ("Unhappy\n");
345 empathy_account_update (account, properties);
349 empathy_account_unescape_protocol (const gchar *protocol, gssize len)
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)
359 str = g_string_new ("");
362 g_string_append_len (str, input, escape - input);
363 g_string_append_c (str, '-');
365 len -= escape - input + 3;
367 } while ((escape = g_strstr_len (input, len, "_2d")) != NULL);
369 g_string_append_len (str, input, len);
371 result = g_string_free (str, FALSE);
375 result = g_strndup (protocol, len);
378 g_strdelimit (result, "_", '-');
384 empathy_account_parse_unique_name (const gchar *bus_name,
385 gchar **protocol, gchar **manager)
387 const gchar *proto, *proto_end;
388 const gchar *cm, *cm_end;
390 g_return_val_if_fail (
391 g_str_has_prefix (bus_name, UNIQUE_NAME_PREFIX), FALSE);
393 cm = bus_name + strlen (UNIQUE_NAME_PREFIX);
395 for (cm_end = cm; *cm_end != '/' && *cm_end != '\0'; cm_end++)
406 for (proto_end = proto; *proto_end != '/' && *proto_end != '\0'; proto_end++)
409 if (*proto_end == '\0')
412 if (protocol != NULL)
414 *protocol = empathy_account_unescape_protocol (proto, proto_end - proto);
418 *manager = g_strndup (cm, cm_end - cm);
424 empathy_account_constructed (GObject *object)
426 EmpathyAccount *account = EMPATHY_ACCOUNT (object);
427 EmpathyAccountPriv *priv = GET_PRIV (account);
429 priv->account = tp_account_new (priv->dbus, priv->unique_name, NULL);
431 empathy_account_parse_unique_name (priv->unique_name,
432 &(priv->proto_name), &(priv->cm_name));
434 priv->icon_name = g_strdup_printf ("im-%s", priv->proto_name);
436 tp_cli_account_connect_to_account_property_changed (priv->account,
437 empathy_account_properties_changed,
438 NULL, NULL, object, NULL);
440 tp_cli_dbus_properties_call_get_all (priv->account, -1,
442 empathy_account_got_all_cb,
448 static void empathy_account_dispose (GObject *object);
449 static void empathy_account_finalize (GObject *object);
452 empathy_account_class_init (EmpathyAccountClass *empathy_account_class)
454 GObjectClass *object_class = G_OBJECT_CLASS (empathy_account_class);
456 g_type_class_add_private (empathy_account_class,
457 sizeof (EmpathyAccountPriv));
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;
465 g_object_class_install_property (object_class, PROP_ENABLED,
466 g_param_spec_boolean ("enabled",
468 "Whether this account is enabled or not",
470 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
472 g_object_class_install_property (object_class, PROP_READY,
473 g_param_spec_boolean ("ready",
475 "Whether this account is ready to be used",
477 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
479 g_object_class_install_property (object_class, PROP_PRESENCE,
480 g_param_spec_uint ("presence",
482 "The account connections presence type",
484 NUM_TP_CONNECTION_PRESENCE_TYPES,
485 TP_CONNECTION_PRESENCE_TYPE_UNSET,
486 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
488 g_object_class_install_property (object_class, PROP_STATUS,
489 g_param_spec_string ("status",
491 "The Status string of the account",
493 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
495 g_object_class_install_property (object_class, PROP_STATUS_MESSAGE,
496 g_param_spec_string ("status-message",
498 "The Status message string of the account",
500 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
502 g_object_class_install_property (object_class, PROP_CONNECTION_STATUS,
503 g_param_spec_uint ("connection-status",
505 "The accounts connections status type",
507 NUM_TP_CONNECTION_STATUSES,
508 TP_CONNECTION_STATUS_DISCONNECTED,
509 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
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",
516 NUM_TP_CONNECTION_STATUS_REASONS,
517 TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
518 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
520 g_object_class_install_property (object_class, PROP_CONNECTION,
521 g_param_spec_object ("connection",
523 "The accounts connection",
525 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
527 g_object_class_install_property (object_class, PROP_UNIQUE_NAME,
528 g_param_spec_string ("unique-name",
530 "The accounts unique name",
532 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
534 g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
535 g_param_spec_object ("dbus-daemon",
537 "The Tp Dbus daemon on which this account exists",
539 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
541 g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
542 g_param_spec_string ("display-name",
544 "The accounts display name",
546 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
548 signals[STATUS_CHANGED] = g_signal_new ("status-changed",
549 G_TYPE_FROM_CLASS (object_class),
552 _empathy_marshal_VOID__UINT_UINT_UINT,
553 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
555 signals[PRESENCE_CHANGED] = g_signal_new ("presence-changed",
556 G_TYPE_FROM_CLASS (object_class),
559 _empathy_marshal_VOID__UINT_STRING_STRING,
560 G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
564 empathy_account_dispose (GObject *object)
566 EmpathyAccount *self = EMPATHY_ACCOUNT (object);
567 EmpathyAccountPriv *priv = GET_PRIV (self);
569 if (priv->dispose_has_run)
572 priv->dispose_has_run = TRUE;
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;
579 if (priv->connection != NULL)
580 g_object_unref (priv->connection);
581 priv->connection = NULL;
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);
589 empathy_account_finalize (GObject *object)
591 EmpathyAccountPriv *priv = GET_PRIV (object);
593 g_free (priv->status);
594 g_free (priv->message);
596 g_free (priv->cm_name);
597 g_free (priv->proto_name);
598 g_free (priv->icon_name);
599 g_free (priv->display_name);
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);
607 empathy_account_is_just_connected (EmpathyAccount *account)
609 EmpathyAccountPriv *priv = GET_PRIV (account);
612 if (priv->connection_status != TP_CONNECTION_STATUS_CONNECTED)
615 g_get_current_time (&val);
617 return (val.tv_sec - priv->connect_time) < 10;
621 * empathy_account_get_connection:
622 * @account: a #EmpathyAccount
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.
627 * Returns: the connection of the account.
630 empathy_account_get_connection (EmpathyAccount *account)
632 EmpathyAccountPriv *priv = GET_PRIV (account);
634 if (priv->connection != NULL &&
635 tp_connection_is_ready (priv->connection))
636 return priv->connection;
642 * empathy_account_get_unique_name:
643 * @account: a #EmpathyAccount
645 * Returns: the unique name of the account.
648 empathy_account_get_unique_name (EmpathyAccount *account)
650 EmpathyAccountPriv *priv = GET_PRIV (account);
652 return priv->unique_name;
656 * empathy_account_get_display_name:
657 * @account: a #EmpathyAccount
659 * Returns: the display name of the account.
662 empathy_account_get_display_name (EmpathyAccount *account)
664 EmpathyAccountPriv *priv = GET_PRIV (account);
666 return priv->display_name;
670 empathy_account_is_valid (EmpathyAccount *account)
672 //EmpathyAccountPriv *priv = GET_PRIV (account);
679 empathy_account_get_connection_manager (EmpathyAccount *account)
681 EmpathyAccountPriv *priv = GET_PRIV (account);
683 return priv->cm_name;
687 empathy_account_get_protocol (EmpathyAccount *account)
689 EmpathyAccountPriv *priv = GET_PRIV (account);
691 return priv->proto_name;
695 empathy_account_get_icon_name (EmpathyAccount *account)
697 EmpathyAccountPriv *priv = GET_PRIV (account);
699 return priv->icon_name;
703 empathy_account_get_parameters (EmpathyAccount *account)
705 EmpathyAccountPriv *priv = GET_PRIV (account);
707 return priv->parameters;
711 empathy_account_is_enabled (EmpathyAccount *account)
713 EmpathyAccountPriv *priv = GET_PRIV (account);
715 return priv->enabled;
719 empathy_account_is_ready (EmpathyAccount *account)
721 EmpathyAccountPriv *priv = GET_PRIV (account);
728 empathy_account_new (TpDBusDaemon *dbus, const gchar *unique_name)
730 return EMPATHY_ACCOUNT (g_object_new (EMPATHY_TYPE_ACCOUNT,
732 "unique-name", unique_name,
737 empathy_account_connection_ready_cb (TpConnection *connection,
741 EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
742 EmpathyAccountPriv *priv = GET_PRIV (account);
746 DEBUG ("(%s) Connection failed to become ready: %s",
747 empathy_account_get_unique_name (account), error->message);
748 priv->connection = NULL;
752 DEBUG ("(%s) Connection ready",
753 empathy_account_get_unique_name (account));
754 g_object_notify (G_OBJECT (account), "connection");
759 _empathy_account_connection_invalidated_cb (TpProxy *self,
765 EmpathyAccount *account = EMPATHY_ACCOUNT (user_data);
766 EmpathyAccountPriv *priv = GET_PRIV (account);
768 if (priv->connection == NULL)
771 DEBUG ("(%s) Connection invalidated",
772 empathy_account_get_unique_name (account));
774 g_assert (priv->connection == TP_CONNECTION (self));
776 g_signal_handler_disconnect (priv->connection,
777 priv->connection_invalidated_id);
778 priv->connection_invalidated_id = 0;
780 g_object_unref (priv->connection);
781 priv->connection = NULL;
783 g_object_notify (G_OBJECT (account), "connection");
787 _empathy_account_set_connection (EmpathyAccount *account,
788 TpConnection *connection)
790 EmpathyAccountPriv *priv = GET_PRIV (account);
792 if (priv->connection == connection)
795 /* Connection already set, don't set the new one */
796 if (connection != NULL && priv->connection != NULL)
799 if (connection == NULL)
801 g_signal_handler_disconnect (priv->connection,
802 priv->connection_invalidated_id);
803 priv->connection_invalidated_id = 0;
805 g_object_unref (priv->connection);
806 priv->connection = NULL;
807 g_object_notify (G_OBJECT (account), "connection");
811 priv->connection = g_object_ref (connection);
812 priv->connection_invalidated_id = g_signal_connect (priv->connection,
814 G_CALLBACK (_empathy_account_connection_invalidated_cb),
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);
825 empathy_account_set_enabled (EmpathyAccount *account,
828 EmpathyAccountPriv *priv = GET_PRIV (account);
829 GValue value = {0, };
831 if (priv->enabled == enabled)
834 g_value_init (&value, G_TYPE_BOOLEAN);
835 g_value_set_boolean (&value, enabled);
837 tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
847 g_value_unset (&value);
851 empathy_account_requested_presence_cb (TpProxy *proxy,
854 GObject *weak_object)
857 DEBUG (":( : %s", error->message);
862 empathy_account_request_presence (EmpathyAccount *account,
863 TpConnectionPresenceType type,
865 const gchar *message)
867 EmpathyAccountPriv *priv = GET_PRIV (account);
868 GValue value = {0, };
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);
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);
880 tp_cli_dbus_properties_call_set (TP_PROXY (priv->account),
885 empathy_account_requested_presence_cb,
890 g_value_unset (&value);
894 empathy_account_updated_cb (TpAccount *proxy,
895 const gchar **reconnect_required,
898 GObject *weak_object)
900 GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
904 g_simple_async_result_set_from_error (result, (GError *) error);
907 g_simple_async_result_complete (result);
908 g_object_unref (G_OBJECT (result));
912 empathy_account_update_settings_async (EmpathyAccount *account,
913 GHashTable *parameters, const gchar **unset_parameters,
914 GAsyncReadyCallback callback, gpointer user_data)
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);
920 tp_cli_account_call_update_parameters (priv->account,
924 empathy_account_updated_cb,
931 empathy_account_update_settings_finish (EmpathyAccount *account,
932 GAsyncResult *result, GError **error)
934 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
938 g_return_val_if_fail (g_simple_async_result_is_valid (result,
939 G_OBJECT (account), empathy_account_update_settings_finish), FALSE);