GQueue *pending_messages_queue;
gboolean had_properties_list;
GPtrArray *properties;
+ TpChannelPasswordFlags password_flags;
+ /* TRUE if we fetched the password flag of the channel or if it's not needed
+ * (channel doesn't implement the Password interface) */
+ gboolean got_password_flags;
gboolean ready;
} EmpathyTpChatPriv;
-typedef struct {
- gchar *name;
- guint id;
- TpPropertyFlags flags;
- GValue *value;
-} TpChatProperty;
-
static void tp_chat_iface_init (EmpathyContactListIface *iface);
enum {
PROP_0,
PROP_CHANNEL,
PROP_REMOTE_CONTACT,
+ PROP_PASSWORD_NEEDED,
PROP_READY,
};
g_list_foreach (members, (GFunc) g_object_ref, NULL);
} else {
members = g_list_prepend (members, g_object_ref (priv->user));
- members = g_list_prepend (members, g_object_ref (priv->remote_contact));
+ if (priv->remote_contact != NULL)
+ members = g_list_prepend (members, g_object_ref (priv->remote_contact));
}
return members;
static void
tp_chat_build_message (EmpathyTpChat *chat,
+ gboolean incoming,
guint id,
guint type,
guint timestamp,
guint from_handle,
- const gchar *message_body)
+ const gchar *message_body,
+ TpChannelTextMessageFlags flags)
{
EmpathyTpChatPriv *priv;
EmpathyMessage *message;
empathy_message_set_receiver (message, priv->user);
empathy_message_set_timestamp (message, timestamp);
empathy_message_set_id (message, id);
+ empathy_message_set_incoming (message, incoming);
+ empathy_message_set_flags (message, flags);
+
g_queue_push_tail (priv->messages_queue, message);
if (from_handle == 0) {
}
tp_chat_build_message (chat,
+ TRUE,
message_id,
message_type,
timestamp,
from_handle,
- message_body);
+ message_body,
+ message_flags);
}
static void
DEBUG ("Message sent: %s", message_body);
tp_chat_build_message (chat,
+ FALSE,
0,
message_type,
timestamp,
0,
- message_body);
+ message_body,
+ 0);
}
static void
if (priv->channel == NULL)
return;
- DEBUG ("Message sent error: %s (%d)", message_body, error_code);
+ DEBUG ("Error sending '%s' (%d)", message_body, error_code);
- tp_chat_build_message (EMPATHY_TP_CHAT (chat),
- 0,
- message_type,
- timestamp,
- 0,
- message_body);
+ g_signal_emit (chat, signals[SEND_ERROR], 0, message_body, error_code);
}
static void
if (error) {
DEBUG ("Error: %s", error->message);
- g_signal_emit (chat, signals[SEND_ERROR], 0, message,
+ g_signal_emit (chat, signals[SEND_ERROR], 0,
+ empathy_message_get_body (message),
TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN);
}
}
}
tp_chat_build_message (chat,
+ TRUE,
message_id,
message_type,
timestamp,
from_handle,
- message_body);
+ message_body,
+ message_flags);
}
if (empty_non_text_content_ids != NULL) {
}
for (i = 0; i < properties->len; i++) {
- GValueArray *prop_struct;
- TpChatProperty *property;
- guint id;
- guint flags;
+ GValueArray *prop_struct;
+ EmpathyTpChatProperty *property;
+ guint id;
+ guint flags;
prop_struct = g_ptr_array_index (properties, i);
id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
}
for (i = 0; i < properties->len; i++) {
- GValueArray *prop_struct;
- TpChatProperty *property;
- guint id;
- GValue *src_value;
+ GValueArray *prop_struct;
+ EmpathyTpChatProperty *property;
+ guint id;
+ GValue *src_value;
prop_struct = g_ptr_array_index (properties, i);
id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), properties->len);
priv->properties = g_ptr_array_sized_new (properties->len);
for (i = 0; i < properties->len; i++) {
- GValueArray *prop_struct;
- TpChatProperty *property;
+ GValueArray *prop_struct;
+ EmpathyTpChatProperty *property;
prop_struct = g_ptr_array_index (properties, i);
- property = g_slice_new0 (TpChatProperty);
+ property = g_slice_new0 (EmpathyTpChatProperty);
property->id = g_value_get_uint (g_value_array_get_nth (prop_struct, 0));
property->name = g_value_dup_string (g_value_array_get_nth (prop_struct, 1));
property->flags = g_value_get_uint (g_value_array_get_nth (prop_struct, 3));
const gchar *name,
const GValue *value)
{
- EmpathyTpChatPriv *priv = GET_PRIV (chat);
- TpChatProperty *property;
- guint i;
+ EmpathyTpChatPriv *priv = GET_PRIV (chat);
+ EmpathyTpChatProperty *property;
+ guint i;
+
+ if (!priv->had_properties_list) {
+ return;
+ }
for (i = 0; i < priv->properties->len; i++) {
property = g_ptr_array_index (priv->properties, i);
}
}
+EmpathyTpChatProperty *
+empathy_tp_chat_get_property (EmpathyTpChat *chat,
+ const gchar *name)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (chat);
+ EmpathyTpChatProperty *property;
+ guint i;
+
+ if (!priv->had_properties_list) {
+ return NULL;
+ }
+
+ for (i = 0; i < priv->properties->len; i++) {
+ property = g_ptr_array_index (priv->properties, i);
+ if (!tp_strdiff (property->name, name)) {
+ return property;
+ }
+ }
+
+ return NULL;
+}
+
+GPtrArray *
+empathy_tp_chat_get_properties (EmpathyTpChat *chat)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+ return priv->properties;
+}
+
static void
tp_chat_dispose (GObject *object)
{
if (priv->properties) {
for (i = 0; i < priv->properties->len; i++) {
- TpChatProperty *property;
+ EmpathyTpChatProperty *property;
property = g_ptr_array_index (priv->properties, i);
g_free (property->name);
if (property->value) {
tp_g_value_slice_free (property->value);
}
- g_slice_free (TpChatProperty, property);
+ g_slice_free (EmpathyTpChatProperty, property);
}
g_ptr_array_free (priv->properties, TRUE);
}
{
EmpathyTpChatPriv *priv = GET_PRIV (chat);
- if (priv->ready || priv->user == NULL ||
- (priv->members == NULL && priv->remote_contact == NULL)) {
+ if (priv->ready)
+ return;
+
+ if (priv->user == NULL)
+ return;
+
+ if (!priv->got_password_flags)
+ return;
+
+ /* We need either the members (room) or the remote contact (private chat).
+ * If the chat is protected by a password we can't get these information so
+ * consider the chat as ready so it can be presented to the user. */
+ if (!empathy_tp_chat_password_needed (chat) && priv->members == NULL &&
+ priv->remote_contact == NULL)
return;
- }
DEBUG ("Ready!");
tp_chat_state_changed_cb,
NULL, NULL,
G_OBJECT (chat), NULL);
- tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
- tp_chat_state_changed_cb,
- NULL, NULL,
- G_OBJECT (chat), NULL);
priv->ready = TRUE;
g_object_notify (G_OBJECT (chat), "ready");
}
static EmpathyContact *
chat_lookup_contact (EmpathyTpChat *chat,
TpHandle handle,
- gboolean remove)
+ gboolean remove_)
{
EmpathyTpChatPriv *priv = GET_PRIV (chat);
GList *l;
continue;
}
- if (remove) {
+ if (remove_) {
/* Caller takes the reference. */
priv->members = g_list_delete_link (priv->members, l);
} else {
return NULL;
}
+typedef struct
+{
+ TpHandle old_handle;
+ guint reason;
+ gchar *message;
+} ContactRenameData;
+
+static ContactRenameData *
+contact_rename_data_new (TpHandle handle,
+ guint reason,
+ const gchar* message)
+{
+ ContactRenameData *data = g_new (ContactRenameData, 1);
+ data->old_handle = handle;
+ data->reason = reason;
+ data->message = g_strdup (message);
+
+ return data;
+}
+
+static void
+contact_rename_data_free (ContactRenameData* data)
+{
+ g_free (data->message);
+ g_free (data);
+}
+
+static void
+tp_chat_got_renamed_contacts_cb (EmpathyTpContactFactory *factory,
+ guint n_contacts,
+ EmpathyContact * const * contacts,
+ guint n_failed,
+ const TpHandle *failed,
+ const GError *error,
+ gpointer user_data,
+ GObject *chat)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (chat);
+ const TpIntSet *members;
+ TpHandle handle;
+ EmpathyContact *old = NULL, *new = NULL;
+ ContactRenameData *rename_data = (ContactRenameData *) user_data;
+
+ if (error) {
+ DEBUG ("Error: %s", error->message);
+ return;
+ }
+
+ /* renamed members can only be delivered one at a time */
+ g_warn_if_fail (n_contacts == 1);
+
+ new = contacts[0];
+
+ members = tp_channel_group_get_members (priv->channel);
+ handle = empathy_contact_get_handle (new);
+
+ old = chat_lookup_contact (EMPATHY_TP_CHAT (chat),
+ rename_data->old_handle, TRUE);
+
+ /* Make sure the contact is still member */
+ if (tp_intset_is_member (members, handle)) {
+ priv->members = g_list_prepend (priv->members,
+ g_object_ref (new));
+
+ if (old != NULL) {
+ g_signal_emit_by_name (chat, "member-renamed",
+ old, new, rename_data->reason,
+ rename_data->message);
+ g_object_unref (old);
+ }
+ }
+
+ tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat));
+ tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
+}
+
+
static void
tp_chat_group_members_changed_cb (TpChannel *self,
gchar *message,
EmpathyContact *contact;
EmpathyContact *actor_contact = NULL;
guint i;
+ ContactRenameData *rename_data;
+ TpHandle old_handle;
+
+ /* Contact renamed */
+ if (reason == TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED) {
+ /* there can only be a single 'added' and a single 'removed' handle */
+ g_warn_if_fail (removed->len == 1);
+ g_warn_if_fail (added->len == 1);
+
+ old_handle = g_array_index (removed, guint, 0);
+
+ rename_data = contact_rename_data_new (old_handle, reason, message);
+ empathy_tp_contact_factory_get_from_handles (priv->factory,
+ added->len, (TpHandle *) added->data,
+ tp_chat_got_renamed_contacts_cb,
+ rename_data, (GDestroyNotify) contact_rename_data_free,
+ G_OBJECT (chat));
+ return;
+ }
if (actor != 0) {
actor_contact = chat_lookup_contact (chat, actor, FALSE);
tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat));
}
+static void
+password_flags_changed_cb (TpChannel *channel,
+ guint added,
+ guint removed,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+ gboolean was_needed, needed;
+
+ was_needed = empathy_tp_chat_password_needed (self);
+
+ priv->password_flags |= added;
+ priv->password_flags ^= removed;
+
+ needed = empathy_tp_chat_password_needed (self);
+
+ if (was_needed != needed)
+ g_object_notify (G_OBJECT (self), "password-needed");
+}
+
+static void
+got_password_flags_cb (TpChannel *proxy,
+ guint password_flags,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ EmpathyTpChat *self = EMPATHY_TP_CHAT (weak_object);
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+ priv->got_password_flags = TRUE;
+ priv->password_flags = password_flags;
+
+ tp_chat_check_if_ready (EMPATHY_TP_CHAT (self));
+}
+
static GObject *
tp_chat_constructor (GType type,
guint n_props,
G_OBJECT (chat), NULL);
}
+ /* Check if the chat is password protected */
+ if (tp_proxy_has_interface_by_id (priv->channel,
+ TP_IFACE_QUARK_CHANNEL_INTERFACE_PASSWORD)) {
+ priv->got_password_flags = FALSE;
+
+ tp_cli_channel_interface_password_connect_to_password_flags_changed
+ (priv->channel, password_flags_changed_cb, chat, NULL,
+ G_OBJECT (chat), NULL);
+
+ tp_cli_channel_interface_password_call_get_password_flags
+ (priv->channel, -1, got_password_flags_cb, chat, NULL, chat);
+ } else {
+ /* No Password interface, so no need to fetch the password flags */
+ priv->got_password_flags = TRUE;
+ }
+
return chat;
}
GValue *value,
GParamSpec *pspec)
{
+ EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
EmpathyTpChatPriv *priv = GET_PRIV (object);
switch (param_id) {
case PROP_READY:
g_value_set_boolean (value, priv->ready);
break;
+ case PROP_PASSWORD_NEEDED:
+ g_value_set_boolean (value, empathy_tp_chat_password_needed (self));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
FALSE,
G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_PASSWORD_NEEDED,
+ g_param_spec_boolean ("password-needed",
+ "password needed",
+ "TRUE if a password is needed to join the channel",
+ FALSE,
+ G_PARAM_READABLE));
+
/* Signals */
signals[MESSAGE_RECEIVED] =
g_signal_new ("message-received",
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- _empathy_marshal_VOID__OBJECT_UINT,
+ _empathy_marshal_VOID__STRING_UINT,
G_TYPE_NONE,
- 2, EMPATHY_TYPE_MESSAGE, G_TYPE_UINT);
+ 2, G_TYPE_STRING, G_TYPE_UINT);
signals[CHAT_STATE_CHANGED] =
g_signal_new ("chat-state-changed",
empathy_tp_chat_get_id (EmpathyTpChat *chat)
{
EmpathyTpChatPriv *priv = GET_PRIV (chat);
+ const gchar *id;
+
g_return_val_if_fail (EMPATHY_IS_TP_CHAT (chat), NULL);
- return tp_channel_get_identifier (priv->channel);
+ id = tp_channel_get_identifier (priv->channel);
+ if (!EMP_STR_EMPTY (id))
+ return id;
+ else if (priv->remote_contact)
+ return empathy_contact_get_id (priv->remote_contact);
+ else
+ return NULL;
+
}
EmpathyContact *
g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
g_return_if_fail (priv->ready);
- if (empathy_message_get_sender (message) == priv->user)
+ if (!empathy_message_is_incoming (message))
goto out;
message_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
g_assert (m != NULL);
g_queue_delete_link (priv->pending_messages_queue, m);
- if (empathy_message_get_sender (message) != priv->user) {
+ if (empathy_message_is_incoming (message)) {
guint id = empathy_message_get_id (message);
g_array_append_val (message_ids, id);
}
g_list_free (msgs);
}
+gboolean
+empathy_tp_chat_password_needed (EmpathyTpChat *self)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+ return priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE;
+}
+
+static void
+provide_password_cb (TpChannel *channel,
+ gboolean correct,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ GSimpleAsyncResult *result = user_data;
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (result, error);
+ }
+ else if (!correct) {
+ /* The current D-Bus API is a bit weird so re-use the
+ * AuthenticationFailed error */
+ g_simple_async_result_set_error (result, TP_ERRORS,
+ TP_ERROR_AUTHENTICATION_FAILED, "Wrong password");
+ }
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+}
+
+void
+empathy_tp_chat_provide_password_async (EmpathyTpChat *self,
+ const gchar *password,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback, user_data,
+ empathy_tp_chat_provide_password_finish);
+
+ tp_cli_channel_interface_password_call_provide_password
+ (priv->channel, -1, password, provide_password_cb, result,
+ NULL, G_OBJECT (self));
+}
+
+gboolean
+empathy_tp_chat_provide_password_finish (EmpathyTpChat *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
+ error))
+ return FALSE;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result,
+ G_OBJECT (self), empathy_tp_chat_provide_password_finish), FALSE);
+
+ return TRUE;
+}