/soc/2015/igor.gajowiak/chatlog: d78ceb0c9faf: Add incoming mess...

Igor Gajowiak igor.gajowiak at gmail.com
Wed Jun 24 13:31:31 EDT 2015


Changeset: d78ceb0c9fafd2e84ac1256aaf5bd467272c1dec
Author:	 Igor Gajowiak <igor.gajowiak at gmail.com>
Date:	 2015-06-20 00:54 +0200
Branch:	 default
URL: https://hg.pidgin.im/soc/2015/igor.gajowiak/chatlog/rev/d78ceb0c9faf

Description:

Add incoming message replacing.

diffstat:

 libpurple/conversation.c                               |  35 ++++++
 libpurple/conversation.h                               |  12 ++
 libpurple/message.c                                    |   5 +
 libpurple/protocol.c                                   |   6 +
 libpurple/protocol.h                                   |   9 +
 libpurple/protocols/jabber/jabber.c                    |   1 +
 libpurple/protocols/jabber/message.c                   |  89 ++++++++++++++++-
 libpurple/protocols/jabber/message.h                   |   1 +
 libpurple/server.c                                     |  20 +++
 libpurple/server.h                                     |   8 +
 pidgin/gtkconv.c                                       |  87 ++++++++++++++++-
 pidgin/gtkconv.h                                       |   2 +
 pidgin/gtkwebview.c                                    |  94 ++++++++++++++++++
 pidgin/themes/Contents/Resources/Outgoing/Content.html |   6 -
 pidgin/themes/Contents/Resources/main.css              |   5 +
 pidgin/themes/Template.html                            |  68 ++++++------
 16 files changed, 403 insertions(+), 45 deletions(-)

diffs (truncated from 759 to 300 lines):

diff --git a/libpurple/conversation.c b/libpurple/conversation.c
--- a/libpurple/conversation.c
+++ b/libpurple/conversation.c
@@ -772,6 +772,7 @@ purple_conversation_replace_message(Purp
 	PurpleConversationClass *klass = NULL;
 
 	g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
+	g_return_if_fail(replaced_msg_id != PURPLE_MESSAGE_ID_NONE);
 	g_return_if_fail(msg != NULL);
 
 	klass = PURPLE_CONVERSATION_GET_CLASS(conv);
@@ -803,6 +804,40 @@ purple_conversation_send_with_flags(Purp
 	common_send(conv, message, flags);
 }
 
+void
+purple_conversation_send_replace_message_with_flags(
+	PurpleConversation *conv, guint replaced_msg_id,
+	const char *new_message, PurpleMessageFlags flags)
+{
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
+	g_return_if_fail(replaced_msg_id != PURPLE_MESSAGE_ID_NONE);
+	g_return_if_fail(new_message != NULL);
+
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	PurpleMessage *msg;
+
+	account = purple_conversation_get_account(conv);
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+	gc = purple_account_get_connection(account);
+	g_return_if_fail(PURPLE_IS_CONNECTION(gc));
+
+	flags |= PURPLE_MESSAGE_SEND;
+
+	if (PURPLE_IS_IM_CONVERSATION(conv)) {
+		msg = purple_message_new_outgoing(
+			purple_conversation_get_name(conv), new_message, flags);
+
+		// Replace message in conversation if sending succeeded
+		if (purple_serv_send_replace_im(gc, replaced_msg_id, msg))
+			purple_conversation_replace_message(conv, replaced_msg_id, msg);
+	}
+	else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
+		// TODO: Do we need to implement this?
+	}
+}
+
 gboolean
 purple_conversation_has_focus(PurpleConversation *conv)
 {
diff --git a/libpurple/conversation.h b/libpurple/conversation.h
--- a/libpurple/conversation.h
+++ b/libpurple/conversation.h
@@ -525,6 +525,18 @@ void purple_conversation_send_with_flags
 		PurpleMessageFlags flags);
 
 /**
+ * purple_conversation_send_replace_message_with_flags:
+ * @conv:            The conversation.
+ * @replaced_msg_id: The id of the PurpleMessage to replace.
+ * @new_message:     The new message data.
+ * @flags:           The PurpleMessageFlags flags to use in addition to
+ *                   PURPLE_MESSAGE_SEND.
+ */
+void purple_conversation_send_replace_message_with_flags(
+	PurpleConversation *conv, guint replaced_msg_id,
+	const char *new_message, PurpleMessageFlags flags);
+
+/**
  * purple_conversation_set_features:
  * @conv:      The conversation
  * @features:  Bitset defining supported features
diff --git a/libpurple/message.c b/libpurple/message.c
--- a/libpurple/message.c
+++ b/libpurple/message.c
@@ -251,6 +251,9 @@ purple_message_init(GTypeInstance *insta
 	PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg);
 	PURPLE_DBUS_REGISTER_POINTER(msg, PurpleMessage);
 
+	purple_signal_register(msg, "purple-message-edit",
+		purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, PURPLE_TYPE_MESSAGE);
+
 	priv->id = generate_next_id();
 	g_hash_table_insert(messages, GINT_TO_POINTER(priv->id), msg);
 }
@@ -261,6 +264,8 @@ purple_message_finalize(GObject *obj)
 	PurpleMessage *message = PURPLE_MESSAGE(obj);
 	PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(message);
 
+	purple_signal_unregister(message, "purple-message-edit");
+
 	g_free(priv->author);
 	g_free(priv->author_alias);
 	g_free(priv->recipient);
diff --git a/libpurple/protocol.c b/libpurple/protocol.c
--- a/libpurple/protocol.c
+++ b/libpurple/protocol.c
@@ -611,6 +611,12 @@ purple_protocol_im_iface_send(PurpleProt
 	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, send, gc, msg);
 }
 
+int purple_protocol_im_iface_replace(PurpleProtocol *protocol, PurpleConnection *gc,
+	guint replaced_msg_id, PurpleMessage *msg)
+{
+	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, replace, gc, replaced_msg_id, msg);
+}
+
 unsigned int
 purple_protocol_im_iface_send_typing(PurpleProtocol *protocol,
 		PurpleConnection *gc, const char *name, PurpleIMTypingState state)
diff --git a/libpurple/protocol.h b/libpurple/protocol.h
--- a/libpurple/protocol.h
+++ b/libpurple/protocol.h
@@ -394,6 +394,10 @@ typedef struct _PurpleProtocolIMIface Pu
  *               negative value. You can use one of the valid #errno values, or
  *               just big something. If the message should not be echoed to the
  *               conversation window, return 0.
+ * @replace      This function should behave exactly the same as @send except
+ *               the fact that it should replace a message instead of sending a new one.
+ *               It is a protocol's responsibility to map PurpleMessage ids to internal
+ *               protocol's message ids and vice versa.
  * @send_typing: If this protocol requires the #PURPLE_IM_TYPING message to be
  *               sent repeatedly to signify that the user is still typing, then
  *               the protocol should return the number of seconds to wait before
@@ -412,6 +416,8 @@ struct _PurpleProtocolIMIface
 	/*< public >*/
 	int  (*send)(PurpleConnection *, PurpleMessage *msg);
 
+	int (*replace)(PurpleConnection *, guint replaced_msg_id, PurpleMessage *msg);
+
 	unsigned int (*send_typing)(PurpleConnection *, const char *name,
 							PurpleIMTypingState state);
 };
@@ -1013,6 +1019,9 @@ GType purple_protocol_im_iface_get_type(
 int purple_protocol_im_iface_send(PurpleProtocol *, PurpleConnection *,
 		 PurpleMessage *msg);
 
+int purple_protocol_im_iface_replace(PurpleProtocol *, PurpleConnection *,
+	guint replaced_msg_id, PurpleMessage *msg);
+
 unsigned int purple_protocol_im_iface_send_typing(PurpleProtocol *,
 		PurpleConnection *, const char *name, PurpleIMTypingState state);
 
diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -4208,6 +4208,7 @@ static void
 jabber_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
 {
 	im_iface->send        = jabber_message_send_im;
+	im_iface->replace     = jabber_message_replace_im;
 	im_iface->send_typing = jabber_send_typing;
 }
 
diff --git a/libpurple/protocols/jabber/message.c b/libpurple/protocols/jabber/message.c
--- a/libpurple/protocols/jabber/message.c
+++ b/libpurple/protocols/jabber/message.c
@@ -841,9 +841,8 @@ void jabber_message_parse(JabberStream *
 			const char *id = purple_xmlnode_get_attrib(child, "id");
 
 			// Some communicators (OneTeam for example)
-			// do not send proper xmlns here, so I skip the check for now
-			// if(id && !strcmp(xmlns, NS_MESSAGE_REPLACE))
-			if(id)
+			// do not send proper xmlns here
+			if(id && !strcmp(xmlns, NS_MESSAGE_REPLACE))
 				jm->replaced_msg_id = g_strdup(id);
 		}
 	}
@@ -1109,6 +1108,12 @@ void jabber_message_send(JabberMessage *
 		purple_xmlnode_insert_data(child, jm->body, -1);
 	}
 
+	if (jm->replaced_msg_id) {
+		child = purple_xmlnode_new_child(message, "replace");
+		purple_xmlnode_set_attrib(child, "id", jm->replaced_msg_id);
+		purple_xmlnode_set_namespace(child, NS_MESSAGE_REPLACE);
+	}
+
 	if(jm->xhtml) {
 		if ((child = purple_xmlnode_from_str(jm->xhtml, -1))) {
 			purple_xmlnode_insert_child(message, child);
@@ -1188,6 +1193,84 @@ int jabber_message_send_im(PurpleConnect
 	jm->to = g_strdup(rcpt);
 	jm->id = jabber_get_next_id(jm->js);
 
+	g_hash_table_insert(prpl_to_jbr, purple_message_get_id(msg), g_strdup(jm->id));
+
+	if(jbr) {
+		if(jbr->thread_id)
+			jm->thread_id = jbr->thread_id;
+
+		if (jbr->chat_states == JABBER_CHAT_STATES_UNSUPPORTED)
+			jm->chat_state = JM_STATE_NONE;
+		else {
+			/* if(JABBER_CHAT_STATES_UNKNOWN == jbr->chat_states)
+			   jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED; */
+		}
+	}
+
+	tmp = purple_utf8_strip_unprintables(purple_message_get_contents(msg));
+	purple_markup_html_to_xhtml(tmp, &xhtml, &jm->body);
+	g_free(tmp);
+
+	tmp = jabber_message_smileyfy_xhtml(jm, xhtml);
+	if (tmp) {
+		g_free(xhtml);
+		xhtml = tmp;
+	}
+
+	/*
+	 * For backward compatibility with user expectations or for those not on
+	 * the user's roster, allow sending XHTML-IM markup.
+	 */
+	if (!jbr || !jbr->caps.info ||
+			jabber_resource_has_capability(jbr, NS_XHTML_IM)) {
+		if (!jabber_xhtml_plain_equal(xhtml, jm->body))
+			/* Wrap the message in <p/> for great interoperability justice. */
+			jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'><p>%s</p></body></html>", xhtml);
+	}
+
+	g_free(xhtml);
+
+	jabber_message_send(jm);
+	jabber_message_free(jm);
+	return 1;
+}
+
+// TODO: copied from jabber_message_send_im, try to refactor
+int jabber_message_replace_im(PurpleConnection *gc, guint replaced_msg_id, PurpleMessage *msg)
+{
+	JabberMessage *jm;
+	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
+	char *xhtml;
+	char *tmp;
+	char *resource;
+	const gchar *rcpt = purple_message_get_recipient(msg);
+	char *msg_id;
+
+	if (!rcpt || purple_message_is_empty(msg))
+		return 0;
+
+	msg_id = (char*) g_hash_table_lookup(prpl_to_jbr, GUINT_TO_POINTER(replaced_msg_id));
+	if (msg_id == NULL)
+		return 0;
+
+	resource = jabber_get_resource(rcpt);
+
+	jb = jabber_buddy_find(purple_connection_get_protocol_data(gc), rcpt, TRUE);
+	jbr = jabber_buddy_find_resource(jb, resource);
+
+	g_free(resource);
+
+	jm = g_new0(JabberMessage, 1);
+	jm->js = purple_connection_get_protocol_data(gc);
+	jm->type = JABBER_MESSAGE_CHAT;
+	jm->chat_state = JM_STATE_ACTIVE;
+	jm->to = g_strdup(rcpt);
+	jm->id = jabber_get_next_id(jm->js);
+	jm->replaced_msg_id = g_strdup(msg_id);
+
+	g_hash_table_insert(prpl_to_jbr, purple_message_get_id(msg), g_strdup(jm->id));
+
 	if(jbr) {
 		if(jbr->thread_id)
 			jm->thread_id = jbr->thread_id;
diff --git a/libpurple/protocols/jabber/message.h b/libpurple/protocols/jabber/message.h
--- a/libpurple/protocols/jabber/message.h
+++ b/libpurple/protocols/jabber/message.h
@@ -71,6 +71,7 @@ void jabber_message_send(JabberMessage *
 
 void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet);
 int jabber_message_send_im(PurpleConnection *gc, PurpleMessage *msg);
+int jabber_message_replace_im(PurpleConnection *gc, guint replaced_msg_id, PurpleMessage *msg);
 int jabber_message_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg);
 
 unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state);
diff --git a/libpurple/server.c b/libpurple/server.c
--- a/libpurple/server.c
+++ b/libpurple/server.c
@@ -159,6 +159,26 @@ int purple_serv_send_im(PurpleConnection
 	return val;
 }
 
+int purple_serv_send_replace_im(PurpleConnection *gc, guint replaced_msg_id, PurpleMessage *msg)
+{
+	g_return_val_if_fail(gc != NULL, -EINVAL);
+	g_return_val_if_fail(replaced_msg_id != PURPLE_MESSAGE_ID_NONE, -EINVAL);
+	g_return_val_if_fail(msg != NULL, -EINVAL);
+
+	PurpleProtocol *protocol = NULL;
+	protocol = purple_connection_get_protocol(gc);
+
+	g_return_val_if_fail(protocol != NULL, -EINVAL);
+	g_return_val_if_fail(PURPLE_PROTOCOL_HAS_IM_IFACE(protocol), -EINVAL);
+
+	int res = -EINVAL;



More information about the Commits mailing list