guint avatar_max_width;
guint avatar_max_height;
guint avatar_max_size;
+ /* can_request_ft and can_request_st are meaningful only if the connection
+ * doesn't implement ContactCapabilities. If it's implemented, we use it to
+ * check if contacts support file transfer and stream tubes. */
gboolean can_request_ft;
gboolean can_request_st;
+ /* TRUE if ContactCapabilities is implemented by the Connection */
+ gboolean contact_caps_supported;
} EmpathyTpContactFactoryPriv;
G_DEFINE_TYPE (EmpathyTpContactFactory, empathy_tp_contact_factory, G_TYPE_OBJECT);
handle);
empathy_contact_load_avatar_data (contact,
- avatar_data->data,
+ (guchar *) avatar_data->data,
avatar_data->len,
mime_type,
token);
return FALSE;
}
-typedef struct {
- EmpathyTpContactFactory *tp_factory;
- GArray *handles;
-} TokensData;
-
-static void
-tp_contact_factory_avatar_tokens_foreach (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- TokensData *data = user_data;
- const gchar *token = value;
- guint handle = GPOINTER_TO_UINT (key);
-
- if (!tp_contact_factory_avatar_maybe_update (data->tp_factory,
- handle, token)) {
- g_array_append_val (data->handles, handle);
- }
-}
-
static void
-tp_contact_factory_got_known_avatar_tokens (EmpathyTpContactFactory *tp_factory,
- GHashTable *tokens,
- const GError *error)
+tp_contact_factory_got_known_avatar_tokens (TpConnection *connection,
+ GHashTable *tokens,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
{
+ EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object);
EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
- TokensData data;
+ GArray *handles;
+ GHashTableIter iter;
+ gpointer key, value;
if (error) {
DEBUG ("Error: %s", error->message);
return;
}
- data.tp_factory = tp_factory;
- data.handles = g_array_new (FALSE, FALSE, sizeof (guint));
- g_hash_table_foreach (tokens,
- tp_contact_factory_avatar_tokens_foreach,
- &data);
+ handles = g_array_new (FALSE, FALSE, sizeof (guint));
+
+ g_hash_table_iter_init (&iter, tokens);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ guint handle = GPOINTER_TO_UINT (key);
+ const gchar *token = value;
+
+ if (!tp_contact_factory_avatar_maybe_update (tp_factory,
+ handle, token)) {
+ g_array_append_val (handles, handle);
+ }
+ }
DEBUG ("Got %d tokens, need to request %d avatars",
- g_hash_table_size (tokens), data.handles->len);
+ g_hash_table_size (tokens), handles->len);
/* Request needed avatars */
- if (data.handles->len > 0) {
+ if (handles->len > 0) {
tp_cli_connection_interface_avatars_call_request_avatars (priv->connection,
-1,
- data.handles,
+ handles,
tp_contact_factory_request_avatars_cb,
NULL, NULL,
G_OBJECT (tp_factory));
}
- g_array_free (data.handles, TRUE);
- g_hash_table_destroy (tokens);
+ g_array_free (handles, TRUE);
}
static void
}
static void
-tp_contact_factory_got_capabilities (EmpathyTpContactFactory *tp_factory,
- GPtrArray *capabilities,
- const GError *error)
+tp_contact_factory_got_capabilities (TpConnection *connection,
+ const GPtrArray *capabilities,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
{
+ EmpathyTpContactFactory *tp_factory;
guint i;
+ tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object);
+
if (error) {
DEBUG ("Error: %s", error->message);
/* FIXME Should set the capabilities of the contacts for which this request
channel_type,
generic,
specific);
-
- g_value_array_free (values);
}
-
- g_ptr_array_free (capabilities, TRUE);
}
#if HAVE_GEOCLUE
tp_contact_factory_update_location (tp_factory, handle, location);
}
+static EmpathyCapabilities
+channel_classes_to_capabilities (GPtrArray *classes,
+ gboolean audio_video)
+{
+ EmpathyCapabilities capabilities = 0;
+ guint i;
+
+ for (i = 0; i < classes->len; i++) {
+ GValueArray *class_struct;
+ GHashTable *fixed_prop;
+ GStrv allowed_prop;
+ TpHandleType handle_type;
+ const gchar *chan_type;
+
+ class_struct = g_ptr_array_index (classes, i);
+ fixed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 0));
+ allowed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 1));
+
+ handle_type = tp_asv_get_uint32 (fixed_prop,
+ TP_IFACE_CHANNEL ".TargetHandleType", NULL);
+ if (handle_type != TP_HANDLE_TYPE_CONTACT)
+ continue;
+
+ chan_type = tp_asv_get_string (fixed_prop,
+ TP_IFACE_CHANNEL ".ChannelType");
+
+ if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) {
+ capabilities |= EMPATHY_CAPABILITIES_FT;
+ }
+
+ else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) {
+ capabilities |= EMPATHY_CAPABILITIES_STREAM_TUBE;
+ }
+ else if (audio_video && !tp_strdiff (chan_type,
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
+ guint j;
+
+ for (j = 0; allowed_prop[j] != NULL; j++) {
+ if (!tp_strdiff (allowed_prop[j],
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"))
+ capabilities |= EMPATHY_CAPABILITIES_AUDIO;
+ else if (!tp_strdiff (allowed_prop[j],
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo"))
+ capabilities |= EMPATHY_CAPABILITIES_VIDEO;
+ }
+ }
+ }
+
+ return capabilities;
+}
+
static void
get_requestable_channel_classes_cb (TpProxy *connection,
const GValue *value,
EmpathyTpContactFactory *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
EmpathyTpContactFactoryPriv *priv = GET_PRIV (self);
GPtrArray *classes;
- guint i;
GList *l;
+ EmpathyCapabilities capabilities;
if (error != NULL) {
DEBUG ("Error: %s", error->message);
}
classes = g_value_get_boxed (value);
- for (i = 0; i < classes->len; i++) {
- GValueArray *class_struct;
- GHashTable *fixed_prop;
- GValue *chan_type, *handle_type;
- class_struct = g_ptr_array_index (classes, i);
- fixed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 0));
-
- handle_type = g_hash_table_lookup (fixed_prop,
- TP_IFACE_CHANNEL ".TargetHandleType");
- if (handle_type == NULL ||
- g_value_get_uint (handle_type) != TP_HANDLE_TYPE_CONTACT)
- continue;
-
- chan_type = g_hash_table_lookup (fixed_prop,
- TP_IFACE_CHANNEL ".ChannelType");
- if (chan_type == NULL)
- continue;
+ DEBUG ("ContactCapabilities is not implemented; use RCC");
+ capabilities = channel_classes_to_capabilities (classes, FALSE);
+ if ((capabilities & EMPATHY_CAPABILITIES_FT) != 0) {
+ DEBUG ("Assume all contacts support FT as CM implements it");
+ priv->can_request_ft = TRUE;
+ }
- if (!tp_strdiff (g_value_get_string (chan_type),
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
- priv->can_request_ft = TRUE;
- else if (!tp_strdiff (g_value_get_string (chan_type),
- TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
- priv->can_request_st = TRUE;
+ if ((capabilities & EMPATHY_CAPABILITIES_STREAM_TUBE) != 0) {
+ DEBUG ("Assume all contacts support stream tubes as CM implements them");
+ priv->can_request_st = TRUE;
}
if (!priv->can_request_ft && !priv->can_request_st)
caps = empathy_contact_get_capabilities (contact);
+ /* ContactCapabilities is not supported; assume all contacts can do file
+ * transfer if it's implemented in the CM */
if (priv->can_request_ft)
caps |= EMPATHY_CAPABILITIES_FT;
}
}
+static void
+update_contact_capabilities (EmpathyTpContactFactory *self,
+ GHashTable *caps)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, caps);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ TpHandle handle = GPOINTER_TO_UINT (key);
+ GPtrArray *classes = value;
+ EmpathyContact *contact;
+ EmpathyCapabilities capabilities;
+
+ contact = tp_contact_factory_find_by_handle (self, handle);
+ if (contact == NULL)
+ continue;
+
+ capabilities = empathy_contact_get_capabilities (contact);
+ capabilities &= ~EMPATHY_CAPABILITIES_UNKNOWN;
+
+ capabilities |= channel_classes_to_capabilities (classes, TRUE);
+
+ DEBUG ("Changing capabilities for contact %s (%d) to %d",
+ empathy_contact_get_id (contact),
+ empathy_contact_get_handle (contact),
+ capabilities);
+
+ empathy_contact_set_capabilities (contact, capabilities);
+ }
+}
+
+static void
+tp_contact_factory_got_contact_capabilities (TpConnection *connection,
+ GHashTable *caps,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ EmpathyTpContactFactory *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
+
+ if (error != NULL) {
+ DEBUG ("Error: %s", error->message);
+ return;
+ }
+
+ update_contact_capabilities (self, caps);
+}
+
static void
tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
EmpathyContact *contact)
TpHandle self_handle;
TpHandle handle;
GArray handles = {(gchar *) &handle, 1};
- GHashTable *tokens;
- GPtrArray *capabilities;
- GError *error = NULL;
EmpathyCapabilities caps;
/* Keep a weak ref to that contact */
caps = empathy_contact_get_capabilities (contact);
/* Set the FT capability */
- if (priv->can_request_ft) {
- caps |= EMPATHY_CAPABILITIES_FT;
- }
+ if (!priv->contact_caps_supported) {
+ /* ContactCapabilities is not supported; assume all contacts can do file
+ * transfer if it's implemented in the CM */
+ if (priv->can_request_ft) {
+ caps |= EMPATHY_CAPABILITIES_FT;
+ }
- /* Set the Stream Tube capability */
- if (priv->can_request_st) {
- caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;
+ /* Set the Stream Tube capability */
+ if (priv->can_request_st) {
+ caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;
+ }
}
empathy_contact_set_capabilities (contact, caps);
empathy_contact_set_is_user (contact, self_handle == handle);
/* FIXME: This should be done by TpContact */
- tp_cli_connection_interface_avatars_run_get_known_avatar_tokens (priv->connection,
- -1,
- &handles,
- &tokens,
- &error,
- NULL);
- tp_contact_factory_got_known_avatar_tokens (tp_factory, tokens, error);
- g_clear_error (&error);
-
- tp_cli_connection_interface_capabilities_run_get_capabilities (priv->connection,
- -1,
- &handles,
- &capabilities,
- &error,
- NULL);
- tp_contact_factory_got_capabilities (tp_factory, capabilities, error);
- g_clear_error (&error);
+ if (tp_proxy_has_interface_by_id (priv->connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS)) {
+ tp_cli_connection_interface_avatars_call_get_known_avatar_tokens (
+ priv->connection, -1, &handles,
+ tp_contact_factory_got_known_avatar_tokens, NULL, NULL,
+ G_OBJECT (tp_factory));
+ }
+
+ if (priv->contact_caps_supported) {
+ tp_cli_connection_interface_contact_capabilities_call_get_contact_capabilities (
+ priv->connection, -1, &handles,
+ tp_contact_factory_got_contact_capabilities, NULL, NULL,
+ G_OBJECT (tp_factory));
+ }
+ else if (tp_proxy_has_interface_by_id (priv->connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES)) {
+ tp_cli_connection_interface_capabilities_call_get_capabilities (
+ priv->connection, -1, &handles,
+ tp_contact_factory_got_capabilities, NULL, NULL,
+ G_OBJECT (tp_factory));
+ }
if (tp_proxy_has_interface_by_id (TP_PROXY (priv->connection),
TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION)) {
contacts_array_free (n_contacts, empathy_contacts);
}
+/* The callback is NOT given a reference to the EmpathyContact objects */
void
empathy_tp_contact_factory_get_from_ids (EmpathyTpContactFactory *tp_factory,
guint n_ids,
error,
data->user_data, weak_object);
}
+
+ if (contact != NULL)
+ g_object_unref (contact);
}
+/* The callback is NOT given a reference to the EmpathyContact objects */
void
empathy_tp_contact_factory_get_from_id (EmpathyTpContactFactory *tp_factory,
const gchar *id,
contacts_array_free (n_contacts, empathy_contacts);
}
+/* The callback is NOT given a reference to the EmpathyContact objects */
void
empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory,
guint n_handles,
EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
GetContactsData *data;
+ if (n_handles == 0) {
+ callback (tp_factory, 0, NULL, 0, NULL, NULL, user_data, weak_object);
+ return;
+ }
+
g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
g_return_if_fail (handles != NULL);
weak_object);
}
+/* The callback is NOT given a reference to the EmpathyContact objects */
static void
get_contact_by_handle_cb (TpConnection *connection,
guint n_contacts,
{
GetContactsData *data = user_data;
EmpathyContact *contact = NULL;
+ GError *err = NULL;
if (n_contacts == 1) {
contact = dup_contact_for_tp_contact (data->tp_factory,
contacts[0]);
}
+ else {
+ if (error == NULL) {
+ /* tp-glib will provide an error only if the whole operation failed,
+ * but not if, for example, the handle was invalid. We create an error
+ * so the caller of empathy_tp_contact_factory_get_from_handle can
+ * rely on the error to check if the operation succeeded or not. */
+
+ err = g_error_new_literal (TP_ERRORS, TP_ERROR_INVALID_HANDLE,
+ "handle is invalid");
+ }
+ else {
+ err = g_error_copy (error);
+ }
+ }
if (data->callback.contact_cb) {
data->callback.contact_cb (data->tp_factory,
contact,
- error,
+ err,
data->user_data, weak_object);
}
+
+ g_clear_error (&err);
+ if (contact != NULL)
+ g_object_unref (contact);
}
void
G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->finalize (object);
}
-static GObject *
-tp_contact_factory_constructor (GType type,
- guint n_props,
- GObjectConstructParam *props)
+static void
+tp_contact_factory_contact_capabilities_changed_cb (TpConnection *connection,
+ GHashTable *caps,
+ gpointer user_data,
+ GObject *weak_object)
{
- GObject *tp_factory;
- EmpathyTpContactFactoryPriv *priv;
+ EmpathyTpContactFactory *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
- tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props);
- priv = GET_PRIV (tp_factory);
+ update_contact_capabilities (self, caps);
+}
+
+static void
+connection_ready_cb (TpConnection *connection,
+ const GError *error,
+ gpointer user_data)
+{
+ EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (user_data);
+ EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
+
+ if (error != NULL)
+ goto out;
/* FIXME: This should be moved to TpContact */
tp_cli_connection_interface_avatars_connect_to_avatar_updated (priv->connection,
tp_contact_factory_avatar_updated_cb,
NULL, NULL,
- tp_factory,
+ G_OBJECT (tp_factory),
NULL);
tp_cli_connection_interface_avatars_connect_to_avatar_retrieved (priv->connection,
tp_contact_factory_avatar_retrieved_cb,
NULL, NULL,
- tp_factory,
+ G_OBJECT (tp_factory),
NULL);
- tp_cli_connection_interface_capabilities_connect_to_capabilities_changed (priv->connection,
- tp_contact_factory_capabilities_changed_cb,
- NULL, NULL,
- tp_factory,
- NULL);
+ if (tp_proxy_has_interface_by_id (connection,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_CAPABILITIES)) {
+ priv->contact_caps_supported = TRUE;
+
+ tp_cli_connection_interface_contact_capabilities_connect_to_contact_capabilities_changed (
+ priv->connection, tp_contact_factory_contact_capabilities_changed_cb,
+ NULL, NULL, G_OBJECT (tp_factory), NULL);
+ }
+ else {
+ tp_cli_connection_interface_capabilities_connect_to_capabilities_changed (priv->connection,
+ tp_contact_factory_capabilities_changed_cb,
+ NULL, NULL,
+ G_OBJECT (tp_factory),
+ NULL);
+ }
tp_cli_connection_interface_location_connect_to_location_updated (priv->connection,
tp_contact_factory_location_updated_cb,
G_OBJECT (tp_factory),
NULL);
- /* FIXME: This should be moved to TpConnection */
tp_cli_connection_interface_avatars_call_get_avatar_requirements (priv->connection,
-1,
tp_contact_factory_got_avatar_requirements_cb,
NULL, NULL,
- tp_factory);
- tp_cli_dbus_properties_call_get (priv->connection, -1,
- TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
- "RequestableChannelClasses",
- get_requestable_channel_classes_cb, NULL, NULL,
- G_OBJECT (tp_factory));
+ G_OBJECT (tp_factory));
+
+ if (!priv->contact_caps_supported) {
+ tp_cli_dbus_properties_call_get (priv->connection, -1,
+ TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
+ "RequestableChannelClasses",
+ get_requestable_channel_classes_cb, NULL, NULL,
+ G_OBJECT (tp_factory));
+ }
+
+out:
+ g_object_unref (tp_factory);
+}
+
+static GObject *
+tp_contact_factory_constructor (GType type,
+ guint n_props,
+ GObjectConstructParam *props)
+{
+ GObject *tp_factory;
+ EmpathyTpContactFactoryPriv *priv;
+
+ tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props);
+ priv = GET_PRIV (tp_factory);
+
+ /* Ensure to keep the self object alive while the call_when_ready is
+ * running */
+ g_object_ref (tp_factory);
+ tp_connection_call_when_ready (priv->connection, connection_ready_cb,
+ tp_factory);
return tp_factory;
}
tp_factory->priv = priv;
priv->can_request_ft = FALSE;
priv->can_request_st = FALSE;
+ priv->contact_caps_supported = FALSE;
}
static GHashTable *factories = NULL;