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
get_requestable_channel_classes_cb (TpProxy *connection,
				    const GValue *value,
				    const GError *error,
				    gpointer user_data,
				    GObject *weak_object)
{
	EmpathyTpContactFactory     *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
	EmpathyTpContactFactoryPriv *priv = GET_PRIV (self);
	GPtrArray                   *classes;
	GList                       *l;
	EmpathyCapabilities         capabilities;

	if (error != NULL) {
		DEBUG ("Error: %s", error->message);
		return;
	}

	classes = g_value_get_boxed (value);

	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 ((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)
		return ;

	/* Update the capabilities of all contacts */
	for (l = priv->contacts; l != NULL; l = g_list_next (l)) {
		EmpathyContact *contact = l->data;
		EmpathyCapabilities caps;

		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;

		if (priv->can_request_st)
			caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;

		empathy_contact_set_capabilities (contact, caps);
	}
}
static void
tp_contact_factory_update_capabilities (EmpathyTpContactFactory *tp_factory,
					guint                    handle,
					const gchar             *channel_type,
					guint                    generic,
					guint                    specific)
{
	EmpathyContact      *contact;
	EmpathyCapabilities  capabilities;

	contact = tp_contact_factory_find_by_handle (tp_factory, handle);
	if (!contact) {
		return;
	}

	capabilities = empathy_contact_get_capabilities (contact);
	capabilities &= ~EMPATHY_CAPABILITIES_UNKNOWN;

	if (strcmp (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) == 0) {
		capabilities &= ~EMPATHY_CAPABILITIES_AUDIO;
		capabilities &= ~EMPATHY_CAPABILITIES_VIDEO;
		if (specific & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) {
			capabilities |= EMPATHY_CAPABILITIES_AUDIO;
		}
		if (specific & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO) {
			capabilities |= EMPATHY_CAPABILITIES_VIDEO;
		}
	}

	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_add_contact (EmpathyTpContactFactory *tp_factory,
				EmpathyContact          *contact)
{
	EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
	TpHandle self_handle;
	TpHandle handle;
	GArray handles = {(gchar *) &handle, 1};
	EmpathyCapabilities caps;

	/* Keep a weak ref to that contact */
	g_object_weak_ref (G_OBJECT (contact),
			   tp_contact_factory_weak_notify,
			   tp_factory);
	priv->contacts = g_list_prepend (priv->contacts, contact);

	/* The contact keeps a ref to its factory */
	g_object_set_data_full (G_OBJECT (contact), "empathy-factory",
				g_object_ref (tp_factory),
				g_object_unref);

	caps = empathy_contact_get_capabilities (contact);

	/* Set the FT capability */
	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;
		}
	}

	empathy_contact_set_capabilities (contact, caps);

	/* Set is-user property. Note that it could still be the handle is
	 * different from the connection's self handle, in the case the handle
	 * comes from a group interface. */
	self_handle = tp_connection_get_self_handle (priv->connection);
	handle = empathy_contact_get_handle (contact);
	empathy_contact_set_is_user (contact, self_handle == handle);

	/* FIXME: This should be done by TpContact */
	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));
	}

	DEBUG ("Contact added: %s (%d)",
		empathy_contact_get_id (contact),
		empathy_contact_get_handle (contact));
}
static void
get_requestable_channel_classes_cb (TpProxy *connection,
				    const GValue *value,
				    const GError *error,
				    gpointer user_data,
				    GObject *weak_object)
{
	EmpathyTpContactFactory     *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
	EmpathyTpContactFactoryPriv *priv = GET_PRIV (self);
	GPtrArray                   *classes;
	guint                        i;
	GList                       *l;

	if (error != NULL) {
		DEBUG ("Error: %s", error->message);
		return;
	}

	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;

		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 (!priv->can_request_ft && !priv->can_request_st)
		return ;

	/* Update the capabilities of all contacts */
	for (l = priv->contacts; l != NULL; l = g_list_next (l)) {
		EmpathyContact *contact = l->data;
		EmpathyCapabilities caps;

		caps = empathy_contact_get_capabilities (contact);

		if (priv->can_request_ft)
			caps |= EMPATHY_CAPABILITIES_FT;

		if (priv->can_request_st)
			caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;

		empathy_contact_set_capabilities (contact, caps);
	}
}