/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