]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-tp-chat.c
add myself to AUTHORS
[empathy.git] / libempathy / empathy-tp-chat.c
index a3282818f09935fe04928e23a12be9ae4c14b2a0..e53caefec5ff5cf500ba0ac3d7290a277f2ca7eb 100644 (file)
@@ -26,6 +26,7 @@
 #include <telepathy-glib/channel.h>
 #include <telepathy-glib/dbus.h>
 #include <telepathy-glib/util.h>
+#include <telepathy-glib/interfaces.h>
 
 #include "empathy-tp-chat.h"
 #include "empathy-tp-contact-factory.h"
@@ -54,22 +55,20 @@ typedef struct {
        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,
 };
 
@@ -163,7 +162,8 @@ tp_chat_get_members (EmpathyContactList *list)
                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;
@@ -226,11 +226,13 @@ tp_chat_got_sender_cb (EmpathyTpContactFactory *factory,
 
 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;
@@ -242,6 +244,9 @@ tp_chat_build_message (EmpathyTpChat *chat,
        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) {
@@ -293,11 +298,13 @@ tp_chat_received_cb (TpChannel   *channel,
        }
 
        tp_chat_build_message (chat,
+                              TRUE,
                               message_id,
                               message_type,
                               timestamp,
                               from_handle,
-                              message_body);
+                              message_body,
+                              message_flags);
 }
 
 static void
@@ -317,11 +324,13 @@ tp_chat_sent_cb (TpChannel   *channel,
        DEBUG ("Message sent: %s", message_body);
 
        tp_chat_build_message (chat,
+                              FALSE,
                               0,
                               message_type,
                               timestamp,
                               0,
-                              message_body);
+                              message_body,
+                              0);
 }
 
 static void
@@ -338,14 +347,9 @@ tp_chat_send_error_cb (TpChannel   *channel,
        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
@@ -358,7 +362,8 @@ tp_chat_send_cb (TpChannel    *proxy,
 
        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);
        }
 }
@@ -459,11 +464,13 @@ tp_chat_list_pending_messages_cb (TpChannel       *channel,
                }
 
                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) {
@@ -489,10 +496,10 @@ tp_chat_property_flags_changed_cb (TpProxy         *proxy,
        }
 
        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));
@@ -527,10 +534,10 @@ tp_chat_properties_changed_cb (TpProxy         *proxy,
        }
 
        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));
@@ -593,11 +600,11 @@ tp_chat_list_properties_cb (TpProxy         *proxy,
        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));
@@ -624,9 +631,13 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat,
                              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);
@@ -668,6 +679,36 @@ empathy_tp_chat_set_property (EmpathyTpChat *chat,
        }
 }
 
+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)
 {
@@ -723,14 +764,14 @@ tp_chat_finalize (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);
        }
@@ -746,10 +787,21 @@ tp_chat_check_if_ready (EmpathyTpChat *chat)
 {
        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!");
 
@@ -776,10 +828,6 @@ tp_chat_check_if_ready (EmpathyTpChat *chat)
                                                                           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");
 }
@@ -878,7 +926,7 @@ tp_chat_got_added_contacts_cb (EmpathyTpContactFactory *factory,
 static EmpathyContact *
 chat_lookup_contact (EmpathyTpChat *chat,
                     TpHandle       handle,
-                    gboolean       remove)
+                    gboolean       remove_)
 {
        EmpathyTpChatPriv *priv = GET_PRIV (chat);
        GList *l;
@@ -890,7 +938,7 @@ chat_lookup_contact (EmpathyTpChat *chat,
                        continue;
                }
 
-               if (remove) {
+               if (remove_) {
                        /* Caller takes the reference. */
                        priv->members = g_list_delete_link (priv->members, l);
                } else {
@@ -903,6 +951,83 @@ chat_lookup_contact (EmpathyTpChat *chat,
        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,
@@ -918,6 +1043,25 @@ tp_chat_group_members_changed_cb (TpChannel     *self,
        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);
@@ -999,6 +1143,44 @@ tp_chat_got_self_contact_cb (EmpathyTpContactFactory *factory,
        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,
@@ -1069,6 +1251,22 @@ tp_chat_constructor (GType                  type,
                                                                               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;
 }
 
@@ -1078,6 +1276,7 @@ tp_chat_get_property (GObject    *object,
                      GValue     *value,
                      GParamSpec *pspec)
 {
+       EmpathyTpChat *self = EMPATHY_TP_CHAT (object);
        EmpathyTpChatPriv *priv = GET_PRIV (object);
 
        switch (param_id) {
@@ -1090,6 +1289,9 @@ tp_chat_get_property (GObject    *object,
        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;
@@ -1150,6 +1352,14 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
                                                               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",
@@ -1167,9 +1377,9 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
                              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",
@@ -1247,10 +1457,19 @@ const gchar *
 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 *
@@ -1328,13 +1547,16 @@ empathy_tp_chat_set_state (EmpathyTpChat      *chat,
        g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
        g_return_if_fail (priv->ready);
 
-       DEBUG ("Set state: %d", state);
-       tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1,
-                                                                state,
-                                                                tp_chat_async_cb,
-                                                                "setting chat state",
-                                                                NULL,
-                                                                G_OBJECT (chat));
+       if (tp_proxy_has_interface_by_id (priv->channel,
+                                         TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE)) {
+               DEBUG ("Set state: %d", state);
+               tp_cli_channel_interface_chat_state_call_set_chat_state (priv->channel, -1,
+                                                                        state,
+                                                                        tp_chat_async_cb,
+                                                                        "setting chat state",
+                                                                        NULL,
+                                                                        G_OBJECT (chat));
+       }
 }
 
 
@@ -1369,7 +1591,7 @@ empathy_tp_chat_acknowledge_message (EmpathyTpChat *chat,
        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);
@@ -1415,7 +1637,7 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
                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);
                }
@@ -1429,3 +1651,66 @@ empathy_tp_chat_acknowledge_messages (EmpathyTpChat *chat,
        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;
+}