soc.2009.telepathy: c0029c6f: Very basic and ugly code for handling ch...

sttwister at soc.pidgin.im sttwister at soc.pidgin.im
Thu Jul 9 07:46:19 EDT 2009


-----------------------------------------------------------------
Revision: c0029c6f3c8cf6f9878e69aa91c76dca6b5a621d
Ancestor: 1d4e05636301557694ccdf3fd50f387e1009b389
Author: sttwister at soc.pidgin.im
Date: 2009-07-09T11:39:17
Branch: im.pidgin.soc.2009.telepathy
URL: http://d.pidgin.im/viewmtn/revision/info/c0029c6f3c8cf6f9878e69aa91c76dca6b5a621d

Modified files:
        libpurple/protocols/telepathy/telepathy.c
        libpurple/protocols/telepathy/telepathy_channel_text.c
        libpurple/protocols/telepathy/telepathy_channel_text.h
        libpurple/protocols/telepathy/telepathy_connection.c
        libpurple/protocols/telepathy/telepathy_connection.h

ChangeLog: 

Very basic and ugly code for handling chatrooms. A lot of the code is
copy-pasted from 1-to-1 IM. Supports joining and receiving messages.

-------------- next part --------------
============================================================
--- libpurple/protocols/telepathy/telepathy.c	334fd4c6d00512ed40eb60f4715d3de09cc2628c
+++ libpurple/protocols/telepathy/telepathy.c	b656e33b1b8c71302d3ad5224538ab11cadede77
@@ -35,6 +35,7 @@
 #include "notify.h"
 #include "plugin.h"
 #include "prefs.h"
+#include "prpl.h"
 #include "util.h"
 #include "version.h"
 
@@ -225,6 +226,21 @@ telepathy_status_types(PurpleAccount *ac
 	return g_list_reverse(types);
 }
 
+static GList *
+telepathy_chat_info(PurpleConnection *gc)
+{
+	GList *m = NULL;
+	struct proto_chat_entry *pce;
+
+	pce = g_new0(struct proto_chat_entry, 1);
+	pce->label = _("_Room name:");
+	pce->identifier = "room";
+	pce->required = TRUE;
+	m = g_list_append(m, pce);
+
+	return m;
+}
+
 static void
 telepathy_login(PurpleAccount *acct)
 {
@@ -542,6 +558,25 @@ static void
 }
 
 static void
+telepathy_join_chat (PurpleConnection *gc, GHashTable *components)
+{
+	telepathy_connection *data = purple_connection_get_protocol_data(gc);
+	const gchar *name = g_hash_table_lookup(components, "room");
+
+	/* Request a room text channel */
+	GHashTable *map = tp_asv_new (
+		TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
+		TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_UINT, TP_HANDLE_TYPE_ROOM,
+		TP_IFACE_CHANNEL ".TargetID", G_TYPE_STRING, name,
+		NULL);
+
+	purple_debug_info("telepathy", "Requesting room text channel for %s\n", name);
+
+	tp_cli_connection_interface_requests_call_ensure_channel(data->connection, -1,
+			map, ensure_channel_cb, data, NULL, NULL);
+}
+
+static void
 telepathy_set_buddy_icon (PurpleConnection *gc, PurpleStoredImage *img)
 {
 	telepathy_connection *data = purple_connection_get_protocol_data(gc);
@@ -604,7 +639,7 @@ static PurplePluginProtocolInfo telepath
 	telepathy_tooltip_text,               /* tooltip_text */
 	telepathy_status_types,               /* status_types */
 	NULL,            /* blist_node_menu */
-	NULL,                  /* chat_info */
+	telepathy_chat_info,                  /* chat_info */
 	NULL,         /* chat_info_defaults */
 	telepathy_login,                      /* login */
 	telepathy_close,                      /* close */
@@ -624,7 +659,7 @@ static PurplePluginProtocolInfo telepath
 	NULL,                 /* rem_permit */
 	NULL,                   /* rem_deny */
 	NULL,            /* set_permit_deny */
-	NULL,                  /* join_chat */
+	telepathy_join_chat,                  /* join_chat */
 	NULL,                /* reject_chat */
 	NULL,              /* get_chat_name */
 	NULL,                /* chat_invite */
============================================================
--- libpurple/protocols/telepathy/telepathy_channel_text.c	88b1a01fd4d38c86a80614d9200dc16ae4b5a3ae
+++ libpurple/protocols/telepathy/telepathy_channel_text.c	d1c55dc11097918d08eb08f5d2781d351d39d2b4
@@ -30,11 +30,364 @@ void
 #include "telepathy_contact.h"
 
 void
+destroy_room_channel(telepathy_room_channel *tp_channel)
+{
+	g_free(tp_channel);
+}
+
+void
 destroy_text_channel(telepathy_text_channel *tp_channel)
 {
 	g_free(tp_channel);
 }
 
+static void
+write_message_to_chatroom (int id,
+                           const gchar *from,
+                           guint timestamp,
+			   const gchar *msg,
+			   gpointer user_data)
+{
+	telepathy_connection *data = user_data;
+
+	/* escape HTML special characters */
+	gchar *escaped_message = g_markup_escape_text(msg, -1);
+
+	/* also change \n to <br> */
+	gchar *final_message = purple_strdup_withhtml(escaped_message);
+	g_free(escaped_message);
+
+	purple_debug_info("telepathy", "Received from %s: \"%s\" (escaped: \"%s\")\n",
+			from, msg, final_message);
+
+	/* transmit the message to the UI */
+	serv_got_chat_in(data->gc, id, from, PURPLE_MESSAGE_RECV, final_message, timestamp);
+
+	g_free(final_message);
+}
+
+static void
+chat_list_pending_messages_cb  (TpChannel *proxy,
+                                const GPtrArray *out_Pending_Messages,
+                                const GError *error,
+                                gpointer user_data,
+                                GObject *weak_object)
+{
+	if (error != NULL)
+	{
+		purple_debug_error("telepathy", "ListPendingMessages error: %s\n", error->message);
+	}
+	else
+	{
+		telepathy_connection *data = user_data;
+		GArray *message_IDs;
+
+		int i;
+
+		GHashTable *properties = tp_channel_borrow_immutable_properties(proxy);
+		gchar *who = (gchar *)tp_asv_get_string(properties, TP_IFACE_CHANNEL ".TargetID");
+		TpHandle handle = tp_channel_get_handle(proxy, NULL);
+
+		/* Get the channel struct by channel handle
+		 * (which is the same with the libpurple chat id)
+		 */
+		telepathy_room_channel *tp_channel = g_hash_table_lookup(
+				data->room_Channels, (gpointer)handle);
+
+		if (tp_channel == NULL)
+		{
+			purple_debug_warning("telepathy", "Pending message from %s,"
+					" but there's no channel struct for the chatroom!\n", who);
+		}
+		else
+		{
+			/* we should now allow received_cb to handle incoming messages */
+			tp_channel->received_Pending_Messages = TRUE;
+		}
+
+		/* this will hold the IDs of message that will be acknowledged */
+		message_IDs = g_array_new(FALSE, FALSE, sizeof(guint));
+
+		for (i = 0; i<out_Pending_Messages->len; ++i)
+		{
+			/* unpack the relevant info from (uuuuus) */
+			GValueArray *arr = g_ptr_array_index(out_Pending_Messages, i);
+			guint msg_id = g_value_get_uint(g_value_array_get_nth(arr, 0));
+			guint timestamp = g_value_get_uint(g_value_array_get_nth(arr, 1));
+			guint sender = g_value_get_uint(g_value_array_get_nth(arr, 2));
+			guint flags = g_value_get_uint(g_value_array_get_nth(arr, 4));
+			gchar *msg = (gchar *)g_value_get_string(g_value_array_get_nth(arr, 5));
+			
+			/* Get information about the contact who sent this message */
+			const gchar *from;
+			telepathy_contact *contact = g_hash_table_lookup(
+					data->contacts, (gpointer)sender);
+
+			if (contact != NULL && contact->contact != NULL)
+				from = tp_contact_get_identifier(contact->contact);
+			else
+				from = "<Unknown Sender>";
+
+			/* drop message if it's not text */
+			if ((flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT) == 0)
+				write_message_to_chatroom(handle, from, timestamp, msg, user_data);
+
+			/* add the id to the array of acknowledge messages */
+			g_array_append_val(message_IDs, msg_id);
+		}
+
+		/* acknowledge the messages now */
+		tp_cli_channel_type_text_call_acknowledge_pending_messages(proxy, -1, message_IDs,
+				acknowledge_pending_messages_cb, user_data, NULL, NULL);
+
+		g_array_free(message_IDs, TRUE);
+
+	}
+}
+
+static void
+chat_received_cb (TpChannel *proxy,
+                  guint arg_ID,
+                  guint arg_Timestamp,
+                  guint arg_Sender,
+                  guint arg_Type,
+                  guint arg_Flags,
+                  const gchar *arg_Text,
+                  gpointer user_data,
+                  GObject *weak_object)
+{
+	telepathy_connection *data = user_data;
+
+	GHashTable *properties = tp_channel_borrow_immutable_properties(proxy);
+	gchar *who = (gchar *)tp_asv_get_string(properties, TP_IFACE_CHANNEL ".TargetID");
+	TpHandle handle = tp_channel_get_handle(proxy, NULL);
+
+	/* Get the channel struct by channel handle
+	 * (which is the same with the libpurple chat id)
+	 */
+	telepathy_room_channel *tp_channel = g_hash_table_lookup(
+			data->room_Channels, (gpointer)handle);
+
+	if (tp_channel == NULL)
+	{
+		purple_debug_warning("telepathy", "Received message from %s,"
+				" but there's no channel struct for the chatroom!\n", who);
+	}
+	else
+	{
+		GArray *message_IDs;
+
+		/* Get information about the sender */
+		telepathy_contact *contact = g_hash_table_lookup(
+				data->contacts, (gpointer)arg_Sender);
+
+		const gchar *from;
+
+		if (contact != NULL)
+			from = tp_contact_get_identifier(contact->contact);
+		else
+			from = "<Unknown>";
+		
+
+		/* will this message get caught by ListPendingMessages? */
+		if (!tp_channel->received_Pending_Messages)
+			return;
+
+		/* drop this message if it's not text */
+		if ((arg_Flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT) == 0)
+			write_message_to_chatroom(handle, from, arg_Timestamp, arg_Text, user_data);
+
+		/* acknowledge receiving the message */
+		message_IDs = g_array_new(FALSE, FALSE, sizeof(guint));
+
+		g_array_append_val(message_IDs, arg_ID);
+
+		tp_cli_channel_type_text_call_acknowledge_pending_messages(proxy, -1, message_IDs,
+				acknowledge_pending_messages_cb, user_data, NULL, NULL);
+
+		g_array_free(message_IDs, TRUE);
+
+	}
+}
+
+static void
+chat_send_error_cb (TpChannel *proxy,
+               guint arg_Error,
+               guint arg_Timestamp,
+               guint arg_Type,
+               const gchar *arg_Text,
+               gpointer user_data,
+               GObject *weak_object)
+{
+	telepathy_connection *data = user_data;
+
+	const gchar *who = tp_channel_get_identifier(proxy);
+
+	const gchar *error_reason = NULL;
+	gchar *error_message;
+	gchar *error_message2;
+
+	switch (arg_Error)
+	{
+		case TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN:
+			error_reason = _("Unknown error");
+		break;
+
+		case TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE:
+			error_reason = _("Contact is offline");
+		break;
+
+		case TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT:
+			error_reason = _("Contact is invalid");
+		break;
+
+		case TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED:
+			error_reason = _("Permission denied");
+		break;
+
+		case TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG:
+			error_reason = _("The message is too long");
+		break;
+
+		case TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED:
+			error_reason = _("Not implemented");
+		break;
+	}
+
+	error_message = g_strdup_printf(_("There was an error sending your message to %s"), who);
+	error_message2 = g_strdup_printf("%s: %s", error_message, error_reason);
+
+	/* FIXME: purple_conv_present_error() only works for 1-to-1 IMs
+	 * use serv_chat_send (PurpleConnection *, int, const char *, PurpleMessageFlags flags)
+	 */
+
+	/* display the error in the conversation */
+	if (!purple_conv_present_error(who, data->acct, error_message2))
+	{
+		/* display as a popup if there is no active conversation with the user */
+		purple_notify_error(purple_connections_get_handle(),
+				_("Error sending message"),
+				error_message,
+				error_reason);
+	}
+
+	g_free(error_message2);
+	g_free(error_message);
+
+	purple_debug_error("telepathy", "SendError: %s\n", error_reason);
+}
+
+static void
+chat_send_cb (TpChannel *proxy,
+              const GError *error,
+              gpointer user_data,
+              GObject *weak_object)
+{
+	if (error != NULL)
+	{
+		telepathy_connection *data = user_data;
+
+		const gchar *who = tp_channel_get_identifier(proxy);
+
+		gchar *error_message = g_strdup_printf(
+				_("There was an error sending your message to %s"), who);
+		gchar *error_message2 = g_strdup_printf("%s: %s", error_message, error->message);
+
+		/* FIXME: purple_conv_present_error() only works for 1-to-1 IMs
+		 * use serv_chat_send (PurpleConnection *, int, const char *, PurpleMessageFlags flags)
+		 */
+
+		/* display the error in the conversation */
+		if (!purple_conv_present_error(who, data->acct, error_message2))
+		{
+			/* display as a popup if there is no active conversation with the user */
+			purple_notify_error(purple_connections_get_handle(),
+					_("Error sending message"),
+					error_message,
+					error->message);
+		}
+
+		g_free(error_message2);
+		g_free(error_message);
+			
+		purple_debug_error("telepathy", "Send error: %s\n", error->message);
+	}
+}
+
+static void
+handle_room_text_channel (TpChannel *channel,
+                          telepathy_connection *data)
+{
+	GError *error = NULL;
+
+	GHashTable *properties = tp_channel_borrow_immutable_properties(channel);
+	gchar *who = (gchar *)tp_asv_get_string(properties, TP_IFACE_CHANNEL ".TargetID");
+	TpHandle handle = tp_channel_get_handle(channel, NULL);
+
+	telepathy_room_channel *tp_channel;
+
+	tp_channel = g_hash_table_lookup(data->room_Channels, (gpointer)handle);
+
+	/* if tp_channel exists, then we requested this channel
+	 * else it's an incoming request so we must cache it
+	 */
+	if (tp_channel == NULL)
+	{
+		purple_debug_info("telepathy", "Saving TpChannel proxy for \"%s\" chatroom\n", who);
+
+		tp_channel = g_new0(telepathy_room_channel, 1);
+		g_hash_table_insert(data->room_Channels, (gpointer)handle, tp_channel);
+		serv_got_joined_chat(data->gc, handle, who);
+	}
+
+	tp_channel->channel = channel;
+
+	tp_cli_channel_type_text_connect_to_received(channel,
+			chat_received_cb, data, NULL, NULL, &error);
+
+	if (error != NULL)
+	{
+		purple_debug_error("telepathy", "Error connecting to Received signal: %s\n",
+				error->message);
+	}
+
+	tp_channel->received_Pending_Messages = FALSE;
+
+
+	g_signal_connect(channel, "invalidated", G_CALLBACK(text_channel_invalidated_cb), data);
+
+	/* the Clear parameter is deprecated, we need to use AcknowledgePendingMessages */
+	tp_cli_channel_type_text_call_list_pending_messages(channel, -1,
+			FALSE, chat_list_pending_messages_cb, data, NULL, NULL);
+
+	/* send pending messages */
+	while (tp_channel->pending_Messages != NULL)
+	{
+		purple_debug_info("telepathy", "Sending pending message \"%s\" to %s\n",
+				(gchar *)tp_channel->pending_Messages->data, who);
+
+		tp_cli_channel_type_text_call_send(channel, -1,
+				TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+				tp_channel->pending_Messages->data,
+				chat_send_cb, data, NULL, NULL);
+
+		/* the message was duped */
+		g_free(tp_channel->pending_Messages->data);
+
+		tp_channel->pending_Messages = g_list_delete_link(
+				tp_channel->pending_Messages, tp_channel->pending_Messages);
+	}
+
+	tp_cli_channel_type_text_connect_to_send_error(channel,
+			chat_send_error_cb, data, NULL, NULL, &error);
+
+	if (error != NULL)
+	{
+		purple_debug_error("telepathy", "Error connecting to SendError signal: %s\n", error->message);
+		g_error_free(error);
+	}
+}
+
 void
 write_message_to_conversation (const gchar *from,
                                guint timestamp,
@@ -353,9 +706,9 @@ chat_state_changed_cb (TpChannel *proxy,
 	}
 }
 
-void
-handle_text_channel (TpChannel *channel,
-                     telepathy_connection *data)
+static void
+handle_im_text_channel (TpChannel *channel,
+                        telepathy_connection *data)
 {
 	GError *error = NULL;
 
@@ -421,6 +774,30 @@ void
 }
 
 void
+handle_text_channel (TpChannel *channel,
+                     telepathy_connection *data)
+{
+	TpHandle handle;
+	TpHandleType handleType;
+
+	handle = tp_channel_get_handle(channel, &handleType);
+
+	switch (handleType)
+	{
+		case TP_HANDLE_TYPE_CONTACT:
+			handle_im_text_channel(channel, data);
+		break;
+
+		case TP_HANDLE_TYPE_ROOM:
+			handle_room_text_channel(channel, data);
+		break;
+
+		default:
+			return;
+	}
+}
+
+void
 set_chat_state_cb (TpChannel *proxy,
                    const GError *error,
                    gpointer user_data,
============================================================
--- libpurple/protocols/telepathy/telepathy_channel_text.h	5f8653276d3406e59f03892e33ab0ede95f16daf
+++ libpurple/protocols/telepathy/telepathy_channel_text.h	23b174dc2096870f8197fcd37cd129f869956170
@@ -34,6 +34,18 @@ typedef struct
 
 	/* This flag avoids having a message processed twice via both Received signal and ListPendingMessages */
 	gboolean received_Pending_Messages;
+} telepathy_room_channel;
+
+void
+destroy_room_channel(telepathy_room_channel *tp_channel);
+
+typedef struct
+{
+	GList *pending_Messages;
+	TpChannel *channel;
+
+	/* This flag avoids having a message processed twice via both Received signal and ListPendingMessages */
+	gboolean received_Pending_Messages;
 } telepathy_text_channel;
 
 void
============================================================
--- libpurple/protocols/telepathy/telepathy_connection.c	93c023c765ce92fbace5f98191c0b530ee5b13db
+++ libpurple/protocols/telepathy/telepathy_connection.c	8ea204f815444131c2c39411766b906b616d6249
@@ -52,10 +52,16 @@ status_changed_cb (TpConnection *proxy,
 
 		purple_connection_set_state(data->gc, PURPLE_CONNECTED);
 
-		data->text_Channels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) destroy_text_channel);
-		data->contacts = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) destroy_contact);
-		data->groups = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) destroy_group);
-		data->lists = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) destroy_group);
+		data->text_Channels = g_hash_table_new_full(g_str_hash, g_str_equal,
+				g_free, (GDestroyNotify) destroy_text_channel);
+		data->room_Channels = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+				NULL, (GDestroyNotify) destroy_room_channel);
+		data->contacts = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+				NULL, (GDestroyNotify) destroy_contact);
+		data->groups = g_hash_table_new_full(g_str_hash, g_str_equal,
+				g_free, (GDestroyNotify) destroy_group);
+		data->lists = g_hash_table_new_full(g_str_hash, g_str_equal,
+				g_free, (GDestroyNotify) destroy_group);
 		data->buddy_to_be_added = g_hash_table_new_full(g_str_hash, g_str_equal,
 				g_free, g_free);
 	}
============================================================
--- libpurple/protocols/telepathy/telepathy_connection.h	768d0676cd74fbc7fe87c50d3e7d0f2c18d99d13
+++ libpurple/protocols/telepathy/telepathy_connection.h	6e2cd4cd44114f9d93f7c2102b42ba3752a9b548
@@ -42,6 +42,9 @@ typedef struct
 	/* This will hold pointers to telepathy_text_channel for buddies that have an active conversation */
 	GHashTable *text_Channels;
 	
+	/* This will map room handles to telepathy_room_channel for active chat rooms */
+	GHashTable *room_Channels;
+	
 	/* This will map contact handles to telepathy_contact */
 	GHashTable *contacts;
 


More information about the Commits mailing list