diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c
index b7c5192..53c4677 100644
--- a/libempathy-gtk/empathy-chat.c
+++ b/libempathy-gtk/empathy-chat.c
@@ -868,7 +868,7 @@ chat_command_me (EmpathyChat *chat,
 		  GStrv        strv)
 {
 	EmpathyChatPriv *priv = GET_PRIV (chat);
-	EmpathyMessage *message;
+	TpMessage *message;
 	TpChannel *channel;
 
 	channel = empathy_tp_chat_get_channel (priv->tp_chat);
@@ -888,12 +888,13 @@ chat_command_me (EmpathyChat *chat,
 
 		tmp = g_strdup_printf ("%s %s", empathy_contact_get_alias (self_contact),
 			strv[1]);
-		message = empathy_message_new (tmp);
+		message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+			tmp);
 		g_free (tmp);
 	}
 	else {
-		message = empathy_message_new (strv[1]);
-		empathy_message_set_tptype (message, TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION);
+		message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
+			strv[1]);
 	}
 
 	empathy_tp_chat_send (priv->tp_chat, message);
@@ -905,9 +906,10 @@ chat_command_say (EmpathyChat *chat,
 		  GStrv        strv)
 {
 	EmpathyChatPriv *priv = GET_PRIV (chat);
-	EmpathyMessage *message;
+	TpMessage *message;
 
-	message = empathy_message_new (strv[1]);
+	message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+		strv[1]);
 	empathy_tp_chat_send (priv->tp_chat, message);
 	g_object_unref (message);
 }
@@ -1074,7 +1076,7 @@ chat_send (EmpathyChat  *chat,
 	   const gchar *msg)
 {
 	EmpathyChatPriv *priv;
-	EmpathyMessage  *message;
+	TpMessage  *message;
 	guint            i;
 
 	if (EMP_STR_EMPTY (msg)) {
@@ -1144,7 +1146,8 @@ chat_send (EmpathyChat  *chat,
 		}
 	}
 
-	message = empathy_message_new (msg);
+	message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+		msg);
 	empathy_tp_chat_send (priv->tp_chat, message);
 	g_object_unref (message);
 }
@@ -1295,9 +1298,14 @@ chat_send_error_cb (EmpathyTpChat          *tp_chat,
 		break;
 	}
 
-	str = g_strdup_printf (_("Error sending message '%s': %s"),
-			       message_body,
-			       error);
+	if (message_body != NULL) {
+			str = g_strdup_printf (_("Error sending message '%s': %s"),
+				message_body, error);
+	}
+	else {
+			str = g_strdup_printf (_("Error sending message: %s"), error);
+	}
+
 	empathy_chat_view_append_event (chat->view, str);
 	g_free (str);
 }
diff --git a/libempathy/empathy-chatroom-manager.c b/libempathy/empathy-chatroom-manager.c
index dd7ef46..9ad75ee 100644
--- a/libempathy/empathy-chatroom-manager.c
+++ b/libempathy/empathy-chatroom-manager.c
@@ -873,6 +873,9 @@ observe_channels_cb (TpSimpleObserver *observer,
       if (tp_proxy_get_invalidated (channel) != NULL)
         continue;
 
+      if (!TP_IS_TEXT_CHANNEL (channel))
+        continue;
+
       tp_chat = empathy_tp_chat_new (account, channel);
       roomname = empathy_tp_chat_get_id (tp_chat);
       chatroom = empathy_chatroom_manager_find (self, account, roomname);
diff --git a/libempathy/empathy-message.c b/libempathy/empathy-message.c
index 65ea2ae..1740e1d 100644
--- a/libempathy/empathy-message.c
+++ b/libempathy/empathy-message.c
@@ -93,28 +93,30 @@ empathy_message_class_init (EmpathyMessageClass *class)
 							    TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
 							    TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
 							    TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
-							    G_PARAM_READWRITE));
+							    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+							    G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (object_class,
 					 PROP_SENDER,
 					 g_param_spec_object ("sender",
 							      "Message Sender",
 							      "The sender of the message",
 							      EMPATHY_TYPE_CONTACT,
-							      G_PARAM_READWRITE));
+							      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 	g_object_class_install_property (object_class,
 					 PROP_RECEIVER,
 					 g_param_spec_object ("receiver",
 							      "Message Receiver",
 							      "The receiver of the message",
 							      EMPATHY_TYPE_CONTACT,
-							      G_PARAM_READWRITE));
+							      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 	g_object_class_install_property (object_class,
 					 PROP_BODY,
 					 g_param_spec_string ("body",
 							      "Message Body",
 							      "The content of the message",
 							      NULL,
-							      G_PARAM_READWRITE));
+							      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+							      G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (object_class,
 					 PROP_TIMESTAMP,
 					 g_param_spec_long ("timestamp",
@@ -123,14 +125,16 @@ empathy_message_class_init (EmpathyMessageClass *class)
 							    -1,
 							    G_MAXLONG,
 							    -1,
-							    G_PARAM_READWRITE));
+							    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+							    G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (object_class,
 					 PROP_IS_BACKLOG,
 					 g_param_spec_boolean ("is-backlog",
 							       "History message",
 							       "If the message belongs to history",
 							       FALSE,
-							       G_PARAM_READWRITE));
+							       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+							       G_PARAM_CONSTRUCT_ONLY));
 
 
 	g_object_class_install_property (object_class,
@@ -139,7 +143,8 @@ empathy_message_class_init (EmpathyMessageClass *class)
 							       "Incoming",
 							       "If this is an incoming (as opposed to sent) message",
 							       FALSE,
-							       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+							       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+							       G_PARAM_CONSTRUCT_ONLY));
 
 	g_object_class_install_property (object_class,
 					 PROP_FLAGS,
@@ -147,7 +152,8 @@ empathy_message_class_init (EmpathyMessageClass *class)
 							       "Flags",
 							       "The TpChannelTextMessageFlags of this message",
 							       0, G_MAXUINT, 0,
-							       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+							       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+							       G_PARAM_CONSTRUCT_ONLY));
 
 	g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
 
@@ -205,9 +211,18 @@ message_get_property (GObject    *object,
 	case PROP_BODY:
 		g_value_set_string (value, priv->body);
 		break;
+	case PROP_TIMESTAMP:
+		g_value_set_long (value, priv->timestamp);
+		break;
+	case PROP_IS_BACKLOG:
+		g_value_set_boolean (value, priv->is_backlog);
+		break;
 	case PROP_INCOMING:
 		g_value_set_boolean (value, priv->incoming);
 		break;
+	case PROP_FLAGS:
+		g_value_set_uint (value, priv->flags);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -226,8 +241,7 @@ message_set_property (GObject      *object,
 
 	switch (param_id) {
 	case PROP_TYPE:
-		empathy_message_set_tptype (EMPATHY_MESSAGE (object),
-					    g_value_get_uint (value));
+		priv->type = g_value_get_uint (value);
 		break;
 	case PROP_SENDER:
 		empathy_message_set_sender (EMPATHY_MESSAGE (object),
@@ -238,12 +252,23 @@ message_set_property (GObject      *object,
 					     EMPATHY_CONTACT (g_value_get_object (value)));
 		break;
 	case PROP_BODY:
-		empathy_message_set_body (EMPATHY_MESSAGE (object),
-					 g_value_get_string (value));
+		g_assert (priv->body == NULL); /* construct only */
+		priv->body = g_value_dup_string (value);
+		break;
+	case PROP_TIMESTAMP:
+		priv->timestamp = g_value_get_long (value);
+		if (priv->timestamp <= 0)
+			priv->timestamp = empathy_time_get_current ();
+		break;
+	case PROP_IS_BACKLOG:
+		priv->is_backlog = g_value_get_boolean (value);
 		break;
 	case PROP_INCOMING:
 		priv->incoming = g_value_get_boolean (value);
 		break;
+	case PROP_FLAGS:
+		priv->flags = g_value_get_uint (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -251,14 +276,6 @@ message_set_property (GObject      *object,
 }
 
 EmpathyMessage *
-empathy_message_new (const gchar *body)
-{
-	return g_object_new (EMPATHY_TYPE_MESSAGE,
-			     "body", body,
-			     NULL);
-}
-
-EmpathyMessage *
 empathy_message_from_tpl_log_event (TplEvent *logevent)
 {
 	EmpathyMessage *retval = NULL;
@@ -303,7 +320,13 @@ empathy_message_from_tpl_log_event (TplEvent *logevent)
 	receiver = tpl_event_get_receiver (logevent);
 	sender = tpl_event_get_sender (logevent);
 
-	retval = empathy_message_new (body);
+	retval = g_object_new (EMPATHY_TYPE_MESSAGE,
+		"type", tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent)),
+		"body", body,
+		"is-backlog", TRUE,
+		"timestamp", tpl_event_get_timestamp (logevent),
+		NULL);
+
 	if (receiver != NULL) {
 		contact = empathy_contact_from_tpl_contact (account, receiver);
 		empathy_message_set_receiver (retval, contact);
@@ -316,12 +339,6 @@ empathy_message_from_tpl_log_event (TplEvent *logevent)
 		g_object_unref (contact);
 	}
 
-	empathy_message_set_timestamp (retval,
-			tpl_event_get_timestamp (logevent));
-        empathy_message_set_tptype (retval,
-            tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent)));
-	empathy_message_set_is_backlog (retval, TRUE);
-
 	g_free (body);
 
 	return retval;
@@ -340,21 +357,6 @@ empathy_message_get_tptype (EmpathyMessage *message)
 	return priv->type;
 }
 
-void
-empathy_message_set_tptype (EmpathyMessage           *message,
-			    TpChannelTextMessageType  type)
-{
-	EmpathyMessagePriv *priv;
-
-	g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
-	priv = GET_PRIV (message);
-
-	priv->type = type;
-
-	g_object_notify (G_OBJECT (message), "type");
-}
-
 EmpathyContact *
 empathy_message_get_sender (EmpathyMessage *message)
 {
@@ -433,25 +435,6 @@ empathy_message_get_body (EmpathyMessage *message)
 	return priv->body;
 }
 
-void
-empathy_message_set_body (EmpathyMessage *message,
-			  const gchar    *body)
-{
-	EmpathyMessagePriv       *priv = GET_PRIV (message);
-
-	g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
-	g_free (priv->body);
-
-	if (body) {
-		priv->body = g_strdup (body);
-	} else {
-		priv->body = NULL;
-	}
-
-	g_object_notify (G_OBJECT (message), "body");
-}
-
 time_t
 empathy_message_get_timestamp (EmpathyMessage *message)
 {
@@ -464,26 +447,6 @@ empathy_message_get_timestamp (EmpathyMessage *message)
 	return priv->timestamp;
 }
 
-void
-empathy_message_set_timestamp (EmpathyMessage *message,
-			       time_t          timestamp)
-{
-	EmpathyMessagePriv *priv;
-
-	g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-	g_return_if_fail (timestamp >= -1);
-
-	priv = GET_PRIV (message);
-
-	if (timestamp <= 0) {
-		priv->timestamp = empathy_time_get_current ();
-	} else {
-		priv->timestamp = timestamp;
-	}
-
-	g_object_notify (G_OBJECT (message), "timestamp");
-}
-
 gboolean
 empathy_message_is_backlog (EmpathyMessage *message)
 {
@@ -496,21 +459,6 @@ empathy_message_is_backlog (EmpathyMessage *message)
 	return priv->is_backlog;
 }
 
-void
-empathy_message_set_is_backlog (EmpathyMessage *message,
-				gboolean is_backlog)
-{
-	EmpathyMessagePriv *priv;
-
-	g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
-	priv = GET_PRIV (message);
-
-	priv->is_backlog = is_backlog;
-
-	g_object_notify (G_OBJECT (message), "is-backlog");
-}
-
 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
 gboolean
 empathy_message_should_highlight (EmpathyMessage *message)
@@ -627,28 +575,6 @@ empathy_message_get_id (EmpathyMessage *message)
 	return priv->id;
 }
 
-void
-empathy_message_set_id (EmpathyMessage *message, guint id)
-{
-	EmpathyMessagePriv *priv = GET_PRIV (message);
-
-	priv->id = id;
-}
-
-void
-empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
-{
-	EmpathyMessagePriv *priv;
-
-	g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
-	priv = GET_PRIV (message);
-
-	priv->incoming = incoming;
-
-	g_object_notify (G_OBJECT (message), "incoming");
-}
-
 gboolean
 empathy_message_is_incoming (EmpathyMessage *message)
 {
@@ -689,17 +615,37 @@ empathy_message_get_flags (EmpathyMessage *self)
 	return priv->flags;
 }
 
-void
-empathy_message_set_flags        (EmpathyMessage           *self,
-				TpChannelTextMessageFlags flags)
+EmpathyMessage *
+empathy_message_new_from_tp_message (TpMessage *tp_msg,
+				     gboolean incoming)
 {
+	EmpathyMessage *message;
 	EmpathyMessagePriv *priv;
+	gchar *body;
+	TpChannelTextMessageFlags flags;
+	guint id;
 
-	g_return_if_fail (EMPATHY_IS_MESSAGE (self));
+	g_return_val_if_fail (TP_IS_MESSAGE (tp_msg), NULL);
 
-	priv = GET_PRIV (self);
+	body = tp_message_to_text (tp_msg, &flags);
 
-	priv->flags = flags;
+	message = g_object_new (EMPATHY_TYPE_MESSAGE,
+		"body", body,
+		"type", tp_message_get_message_type (tp_msg),
+		"timestamp", tp_message_get_received_timestamp (tp_msg),
+		"flags", flags,
+		"is-backlog", flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK,
+		"incoming", incoming,
+		NULL);
 
-	g_object_notify (G_OBJECT (self), "flags");
+	/* FIXME: this is pretty low level, ideally we shouldn't have to use the
+	 * ID directly but we don't use TpTextChannel's ack API everywhere yet. */
+	id = tp_asv_get_uint32 (tp_message_peek (tp_msg, 0),
+		"pending-message-id", NULL);
+
+	priv = GET_PRIV (message);
+	priv->id = id;
+
+	g_free (body);
+	return message;
 }
diff --git a/libempathy/empathy-message.h b/libempathy/empathy-message.h
index 1db0eff..bba65ae 100644
--- a/libempathy/empathy-message.h
+++ b/libempathy/empathy-message.h
@@ -26,6 +26,7 @@
 #define __EMPATHY_MESSAGE_H__
 
 #include <glib-object.h>
+#include <telepathy-glib/message.h>
 #include <telepathy-logger/event.h>
 
 #include "empathy-contact.h"
@@ -53,11 +54,12 @@ struct _EmpathyMessageClass {
 };
 
 GType                    empathy_message_get_type          (void) G_GNUC_CONST;
-EmpathyMessage *         empathy_message_new               (const gchar              *body);
+
 EmpathyMessage *         empathy_message_from_tpl_log_event (TplEvent                *logevent);
+EmpathyMessage *         empathy_message_new_from_tp_message (TpMessage *tp_msg,
+							      gboolean incoming);
+
 TpChannelTextMessageType empathy_message_get_tptype        (EmpathyMessage           *message);
-void                     empathy_message_set_tptype        (EmpathyMessage           *message,
-							    TpChannelTextMessageType  type);
 EmpathyContact *         empathy_message_get_sender        (EmpathyMessage           *message);
 void                     empathy_message_set_sender        (EmpathyMessage           *message,
 							    EmpathyContact           *contact);
@@ -65,30 +67,19 @@ EmpathyContact *         empathy_message_get_receiver      (EmpathyMessage
 void                     empathy_message_set_receiver      (EmpathyMessage           *message,
 							    EmpathyContact           *contact);
 const gchar *            empathy_message_get_body          (EmpathyMessage           *message);
-void                     empathy_message_set_body          (EmpathyMessage           *message,
-							    const gchar              *body);
 time_t                   empathy_message_get_timestamp     (EmpathyMessage           *message);
-void                     empathy_message_set_timestamp     (EmpathyMessage           *message,
-							    time_t                    timestamp);
 gboolean                 empathy_message_is_backlog        (EmpathyMessage           *message);
-void                     empathy_message_set_is_backlog    (EmpathyMessage           *message,
-							    gboolean                 is_backlog);
 gboolean                 empathy_message_is_incoming       (EmpathyMessage           *message);
-void                     empathy_message_set_incoming      (EmpathyMessage           *message,
-							    gboolean                 incoming);
 
 gboolean                 empathy_message_should_highlight  (EmpathyMessage           *message);
 TpChannelTextMessageType empathy_message_type_from_str     (const gchar              *type_str);
 const gchar *            empathy_message_type_to_str       (TpChannelTextMessageType  type);
 
 guint                    empathy_message_get_id (EmpathyMessage *message);
-void                     empathy_message_set_id (EmpathyMessage *message, guint id);
 
 gboolean                 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2);
 
 TpChannelTextMessageFlags empathy_message_get_flags        (EmpathyMessage           *message);
-void                      empathy_message_set_flags        (EmpathyMessage           *message,
-							    TpChannelTextMessageFlags flags);
 
 G_END_DECLS
 
diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c
index 1b2fe46..a56c08a 100644
--- a/libempathy/empathy-tp-chat.c
+++ b/libempathy/empathy-tp-chat.c
@@ -47,7 +47,6 @@ typedef struct {
 	EmpathyContact        *remote_contact;
 	GList                 *members;
 	TpChannel             *channel;
-	gboolean               listing_pending_messages;
 	/* Queue of messages not signalled yet */
 	GQueue                *messages_queue;
 	/* Queue of messages signalled but not acked yet */
@@ -280,149 +279,176 @@ tp_chat_got_sender_cb (TpConnection            *connection,
 
 static void
 tp_chat_build_message (EmpathyTpChat *chat,
-		       gboolean       incoming,
-		       guint          id,
-		       guint          type,
-		       guint          timestamp,
-		       guint          from_handle,
-		       const gchar   *message_body,
-		       TpChannelTextMessageFlags flags)
+		       TpMessage     *msg,
+		       gboolean       incoming)
 {
 	EmpathyTpChatPriv *priv;
 	EmpathyMessage    *message;
+	TpContact *sender;
 
 	priv = GET_PRIV (chat);
 
-	message = empathy_message_new (message_body);
-	empathy_message_set_tptype (message, type);
+	message = empathy_message_new_from_tp_message (msg, incoming);
+	/* FIXME: this is actually a lie for incoming messages. */
 	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);
-
-	if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK)
-		empathy_message_set_is_backlog (message, TRUE);
 
 	g_queue_push_tail (priv->messages_queue, message);
 
-	if (from_handle == 0) {
+	sender = tp_signalled_message_get_sender (msg);
+	g_assert (sender != NULL);
+
+	if (tp_contact_get_handle (sender) == 0) {
 		empathy_message_set_sender (message, priv->user);
 		tp_chat_emit_queued_messages (chat);
 	} else {
 		empathy_tp_contact_factory_get_from_handle (priv->connection,
-			from_handle,
+			tp_contact_get_handle (sender),
 			tp_chat_got_sender_cb,
 			message, NULL, G_OBJECT (chat));
 	}
 }
 
 static void
-tp_chat_received_cb (TpChannel   *channel,
-		     guint        message_id,
-		     guint        timestamp,
-		     guint        from_handle,
-		     guint        message_type,
-		     guint        message_flags,
-		     const gchar *message_body,
-		     gpointer     user_data,
-		     GObject     *chat_)
-{
-	EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
-	EmpathyTpChatPriv *priv = GET_PRIV (chat);
+handle_delivery_report (EmpathyTpChat *self,
+		TpMessage *message)
+{
+	EmpathyTpChatPriv *priv = GET_PRIV (self);
+	TpDeliveryStatus delivery_status;
+	const GHashTable *header;
+	TpChannelTextSendError delivery_error;
+	gboolean valid;
+	GPtrArray *echo;
+	const gchar *message_body = NULL;
+
+	header = tp_message_peek (message, 0);
+	if (header == NULL)
+		goto out;
 
-	if (priv->channel == NULL)
-		return;
+	delivery_status = tp_asv_get_uint32 (header, "delivery-status", &valid);
+	if (!valid || delivery_status != TP_DELIVERY_STATUS_PERMANENTLY_FAILED)
+		goto out;
+
+	delivery_error = tp_asv_get_uint32 (header, "delivery-error", &valid);
+	if (!valid)
+		delivery_error = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
 
-	if (priv->listing_pending_messages) {
+	/* TODO: ideally we should use tp-glib API giving us the echoed message as a
+	 * TpMessage. (fdo #35884) */
+	echo = tp_asv_get_boxed (header, "delivery-echo",
+		TP_ARRAY_TYPE_MESSAGE_PART_LIST);
+	if (echo != NULL && echo->len >= 1) {
+		const GHashTable *echo_body;
+
+		echo_body = g_ptr_array_index (echo, 1);
+		if (echo_body != NULL)
+			message_body = tp_asv_get_string (echo_body, "content");
+	}
+
+	g_signal_emit (self, signals[SEND_ERROR], 0, message_body, delivery_error);
+
+out:
+	tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (priv->channel),
+		message, NULL, NULL);
+}
+
+static void
+handle_incoming_message (EmpathyTpChat *self,
+			 TpMessage *message,
+			 gboolean pending)
+{
+	EmpathyTpChatPriv *priv = GET_PRIV (self);
+	gchar *message_body;
+
+	if (tp_message_is_delivery_report (message)) {
+		handle_delivery_report (self, message);
 		return;
 	}
 
-	DEBUG ("Message received from channel %s: %s",
-		tp_proxy_get_object_path (channel), message_body);
+	message_body = tp_message_to_text (message, NULL);
 
-	if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
-	    !tp_strdiff (message_body, "")) {
-		GArray *ids;
+	DEBUG ("Message %s (channel %s): %s",
+		pending ? "pending" : "received",
+		tp_proxy_get_object_path (priv->channel), message_body);
 
+	if (message_body == NULL) {
 		DEBUG ("Empty message with NonTextContent, ignoring and acking.");
 
-		ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
-		g_array_append_val (ids, message_id);
-		acknowledge_messages (chat, ids);
-		g_array_free (ids, TRUE);
-
+		tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (priv->channel),
+			message, NULL, NULL);
 		return;
 	}
 
-	tp_chat_build_message (chat,
-			       TRUE,
-			       message_id,
-			       message_type,
-			       timestamp,
-			       from_handle,
-			       message_body,
-			       message_flags);
+	tp_chat_build_message (self, message, TRUE);
+
+	g_free (message_body);
 }
 
 static void
-tp_chat_sent_cb (TpChannel   *channel,
-		 guint        timestamp,
-		 guint        message_type,
-		 const gchar *message_body,
-		 gpointer     user_data,
-		 GObject     *chat_)
-{
-	EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
-	EmpathyTpChatPriv *priv = GET_PRIV (chat);
+message_received_cb (TpTextChannel   *channel,
+		     TpMessage *message,
+		     EmpathyTpChat *chat)
+{
+	handle_incoming_message (chat, message, FALSE);
+}
 
-	if (priv->channel == NULL)
-		return;
+static void
+message_sent_cb (TpTextChannel   *channel,
+		 TpMessage *message,
+		 TpMessageSendingFlags flags,
+		 gchar              *token,
+		 EmpathyTpChat      *chat)
+{
+	gchar *message_body;
+
+	message_body = tp_message_to_text (message, NULL);
 
 	DEBUG ("Message sent: %s", message_body);
 
-	tp_chat_build_message (chat,
-			       FALSE,
-			       0,
-			       message_type,
-			       timestamp,
-			       0,
-			       message_body,
-			       0);
+	tp_chat_build_message (chat, message, FALSE);
+
+	g_free (message_body);
 }
 
-static void
-tp_chat_send_error_cb (TpChannel   *channel,
-		       guint        error_code,
-		       guint        timestamp,
-		       guint        message_type,
-		       const gchar *message_body,
-		       gpointer     user_data,
-		       GObject     *chat)
+static TpChannelTextSendError
+error_to_text_send_error (GError *error)
 {
-	EmpathyTpChatPriv *priv = GET_PRIV (chat);
-
-	if (priv->channel == NULL)
-		return;
+	if (error->domain != TP_ERRORS)
+		return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
 
-	DEBUG ("Error sending '%s' (%d)", message_body, error_code);
+	switch (error->code) {
+		case TP_ERROR_OFFLINE:
+			return TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE;
+		case TP_ERROR_INVALID_HANDLE:
+			return TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT;
+		case TP_ERROR_PERMISSION_DENIED:
+			return TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED;
+		case TP_ERROR_NOT_IMPLEMENTED:
+			return TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED;
+	}
 
-	g_signal_emit (chat, signals[SEND_ERROR], 0, message_body, error_code);
+	return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
 }
 
 static void
-tp_chat_send_cb (TpChannel    *proxy,
-		 const GError *error,
-		 gpointer      user_data,
-		 GObject      *chat)
+message_send_cb (GObject *source,
+		 GAsyncResult *result,
+		 gpointer      user_data)
 {
-	EmpathyMessage *message = EMPATHY_MESSAGE (user_data);
+	EmpathyTpChat *chat = user_data;
+	TpTextChannel *channel = (TpTextChannel *) source;
+	GError *error = NULL;
 
-	if (error) {
+	if (!tp_text_channel_send_message_finish (channel, result, NULL, &error)) {
 		DEBUG ("Error: %s", error->message);
+
+		/* FIXME: we should use the body of the message as first argument of the
+		 * signal but can't easily get it as we just get a user_data pointer. Once
+		 * we'll have rebased EmpathyTpChat on top of TpTextChannel we'll be able
+		 * to use the user_data pointer to pass the message and fix this. */
 		g_signal_emit (chat, signals[SEND_ERROR], 0,
-			       empathy_message_get_body (message),
-			       TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN);
+			       NULL, error_to_text_send_error (error));
+
+		g_error_free (error);
 	}
 }
 
@@ -457,86 +483,33 @@ static void
 tp_chat_state_changed_cb (TpChannel *channel,
 			  TpHandle   handle,
 			  TpChannelChatState state,
-			  gpointer   user_data,
-			  GObject   *chat)
+			  EmpathyTpChat *chat)
 {
 	EmpathyTpChatPriv *priv = GET_PRIV (chat);
 
 	empathy_tp_contact_factory_get_from_handle (priv->connection, handle,
 		tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state),
-		NULL, chat);
+		NULL, G_OBJECT (chat));
 }
 
 static void
-tp_chat_list_pending_messages_cb (TpChannel       *channel,
-				  const GPtrArray *messages_list,
-				  const GError    *error,
-				  gpointer         user_data,
-				  GObject         *chat_)
+list_pending_messages (EmpathyTpChat *self)
 {
-	EmpathyTpChat     *chat = EMPATHY_TP_CHAT (chat_);
-	EmpathyTpChatPriv *priv = GET_PRIV (chat);
-	guint              i;
-	GArray            *empty_non_text_content_ids = NULL;
-
-	priv->listing_pending_messages = FALSE;
-
-	if (priv->channel == NULL)
-		return;
-
-	if (error) {
-		DEBUG ("Error listing pending messages: %s", error->message);
-		return;
-	}
-
-	for (i = 0; i < messages_list->len; i++) {
-		GValueArray    *message_struct;
-		const gchar    *message_body;
-		guint           message_id;
-		guint           timestamp;
-		guint           from_handle;
-		guint           message_type;
-		guint           message_flags;
-
-		message_struct = g_ptr_array_index (messages_list, i);
-
-		message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
-		timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
-		from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
-		message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
-		message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
-		message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
-
-		DEBUG ("Message pending: %s", message_body);
+	EmpathyTpChatPriv *priv = GET_PRIV (self);
+	GList *messages, *l;
 
-		if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
-		    !tp_strdiff (message_body, "")) {
-			DEBUG ("Empty message with NonTextContent, ignoring and acking.");
+	g_assert (priv->channel != NULL);
 
-			if (empty_non_text_content_ids == NULL) {
-				empty_non_text_content_ids = g_array_new (FALSE, FALSE, sizeof (guint));
-			}
+	messages = tp_text_channel_get_pending_messages (
+		TP_TEXT_CHANNEL (priv->channel));
 
-			g_array_append_val (empty_non_text_content_ids, message_id);
-			continue;
-		}
+	for (l = messages; l != NULL; l = g_list_next (l)) {
+		TpMessage *message = l->data;
 
-		tp_chat_build_message (chat,
-				       TRUE,
-				       message_id,
-				       message_type,
-				       timestamp,
-				       from_handle,
-				       message_body,
-				       message_flags);
+		handle_incoming_message (self, message, FALSE);
 	}
 
-	if (empty_non_text_content_ids != NULL) {
-		acknowledge_messages (chat, empty_non_text_content_ids);
-		g_array_free (empty_non_text_content_ids, TRUE);
-	}
-
-	check_ready (chat);
+	g_list_free (messages);
 }
 
 static void
@@ -861,32 +834,22 @@ check_almost_ready (EmpathyTpChat *chat)
 	    priv->remote_contact == NULL)
 		return;
 
-	tp_cli_channel_type_text_connect_to_received (priv->channel,
-						      tp_chat_received_cb,
-						      NULL, NULL,
-						      G_OBJECT (chat), NULL);
-	priv->listing_pending_messages = TRUE;
-
-	/* TpChat will be ready once ListPendingMessages returned and all the messages
-	 * have been added to the pending messages queue. */
-	tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1,
-							     FALSE,
-							     tp_chat_list_pending_messages_cb,
-							     NULL, NULL,
-							     G_OBJECT (chat));
-
-	tp_cli_channel_type_text_connect_to_sent (priv->channel,
-						  tp_chat_sent_cb,
-						  NULL, NULL,
-						  G_OBJECT (chat), NULL);
-	tp_cli_channel_type_text_connect_to_send_error (priv->channel,
-							tp_chat_send_error_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);
+	/* We use the default factory so this feature should have been prepared */
+	g_assert (tp_proxy_is_prepared (priv->channel,
+		TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES));
+
+	tp_g_signal_connect_object (priv->channel, "message-received",
+		G_CALLBACK (message_received_cb), chat, 0);
+
+	list_pending_messages (chat);
+
+	tp_g_signal_connect_object (priv->channel, "message-sent",
+		G_CALLBACK (message_sent_cb), chat, 0);
+
+	tp_g_signal_connect_object (priv->channel, "chat-state-changed",
+		G_CALLBACK (tp_chat_state_changed_cb), chat, 0);
+
+	check_ready (chat);
 }
 
 static void
@@ -1551,7 +1514,7 @@ empathy_tp_chat_new (TpAccount *account,
 		     TpChannel *channel)
 {
 	g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
-	g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
+	g_return_val_if_fail (TP_IS_TEXT_CHANNEL (channel), NULL);
 
 	return g_object_new (EMPATHY_TYPE_TP_CHAT,
 			     "account", account,
@@ -1630,27 +1593,23 @@ empathy_tp_chat_is_ready (EmpathyTpChat *chat)
 
 void
 empathy_tp_chat_send (EmpathyTpChat *chat,
-		      EmpathyMessage *message)
+		      TpMessage *message)
 {
 	EmpathyTpChatPriv        *priv = GET_PRIV (chat);
-	const gchar              *message_body;
-	TpChannelTextMessageType  message_type;
+	gchar *message_body;
 
 	g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
-	g_return_if_fail (EMPATHY_IS_MESSAGE (message));
+	g_return_if_fail (TP_IS_CLIENT_MESSAGE (message));
 	g_return_if_fail (priv->ready);
 
-	message_body = empathy_message_get_body (message);
-	message_type = empathy_message_get_tptype (message);
+	message_body = tp_message_to_text (message, NULL);
 
 	DEBUG ("Sending message: %s", message_body);
-	tp_cli_channel_type_text_call_send (priv->channel, -1,
-					    message_type,
-					    message_body,
-					    tp_chat_send_cb,
-					    g_object_ref (message),
-					    (GDestroyNotify) g_object_unref,
-					    G_OBJECT (chat));
+
+	tp_text_channel_send_message_async (TP_TEXT_CHANNEL (priv->channel),
+		message, 0, message_send_cb, chat);
+
+	g_free (message_body);
 }
 
 void
diff --git a/libempathy/empathy-tp-chat.h b/libempathy/empathy-tp-chat.h
index f7998e0..7c998a3 100644
--- a/libempathy/empathy-tp-chat.h
+++ b/libempathy/empathy-tp-chat.h
@@ -69,7 +69,7 @@ TpAccount    * empathy_tp_chat_get_account          (EmpathyTpChat      *chat);
 TpConnection * empathy_tp_chat_get_connection       (EmpathyTpChat      *chat);
 gboolean       empathy_tp_chat_is_ready             (EmpathyTpChat      *chat);
 void           empathy_tp_chat_send                 (EmpathyTpChat      *chat,
-						     EmpathyMessage     *message);
+						     TpMessage     *message);
 void           empathy_tp_chat_set_state            (EmpathyTpChat      *chat,
 						     TpChannelChatState  state);
 void           empathy_tp_chat_set_property         (EmpathyTpChat      *chat,
diff --git a/src/empathy-chat-manager.c b/src/empathy-chat-manager.c
index ff715fe..6c92c25 100644
--- a/src/empathy-chat-manager.c
+++ b/src/empathy-chat-manager.c
@@ -231,6 +231,13 @@ handle_channels (TpSimpleHandler *handler,
       if (tp_proxy_get_invalidated (channel) != NULL)
         continue;
 
+      if (!TP_IS_TEXT_CHANNEL (channel))
+        {
+          DEBUG ("Channel %s doesn't implement Messages; can't handle it",
+              tp_proxy_get_object_path (channel));
+          continue;
+        }
+
       handling = TRUE;
 
       tp_chat = empathy_tp_chat_new (account, channel);
diff --git a/src/empathy-event-manager.c b/src/empathy-event-manager.c
index 342d3e1..6125ec8 100644
--- a/src/empathy-event-manager.c
+++ b/src/empathy-event-manager.c
@@ -934,7 +934,7 @@ approve_channels (TpSimpleApprover *approver,
 
   channel_type = tp_channel_get_channel_type_id (channel);
 
-  if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
+  if (TP_IS_TEXT_CHANNEL (channel))
     {
       EmpathyTpChat *tp_chat;
 
