libpurple 32530:1836d0d921db: jabber: Add support for XEP-0280: ...

commits at adium.im commits at adium.im
Thu May 23 19:47:59 UTC 2013


details:	http://hg.adium.im/libpurple/rev/1836d0d921db
revision:	32530:1836d0d921db
branch:		adium
author:		Thijs Alkemade <me at thijsalkema.de>
date:		Thu May 23 21:47:32 2013 +0200

jabber: Add support for XEP-0280: Message Carbons, allowing clients signed in to an XMPP account to receive all messages, not just those addressed to it.

diffs (345 lines):

diff -r c8e809dffa1d -r 1836d0d921db libpurple/protocols/jabber/disco.c
--- a/libpurple/protocols/jabber/disco.c	Thu Feb 21 23:07:08 2013 +0100
+++ b/libpurple/protocols/jabber/disco.c	Thu May 23 21:47:32 2013 +0200
@@ -295,9 +295,8 @@
 					capabilities |= JABBER_CAP_PING;
 				else if(!strcmp(var, NS_DISCO_ITEMS))
 					capabilities |= JABBER_CAP_ITEMS;
-				else if(!strcmp(var, "http://jabber.org/protocol/commands")) {
+				else if(!strcmp(var, "http://jabber.org/protocol/commands"))
 					capabilities |= JABBER_CAP_ADHOC;
-				}
 				else if(!strcmp(var, NS_IBB)) {
 					purple_debug_info("jabber", "remote supports IBB\n");
 					capabilities |= JABBER_CAP_IBB;
@@ -386,6 +385,17 @@
 		jabber_request_block_list(js);
 	}
 
+	if ((js->server_caps & JABBER_CAP_CARBONS) && purple_account_get_bool(js->gc->account, "carbons", FALSE)) {
+		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
+		xmlnode *enable = xmlnode_new_child(iq->node, "enable");
+		
+		purple_debug_info("jabber", "Automatically enabling Carbons.\n");
+
+		xmlnode_set_namespace(enable, NS_XMPP_CARBONS);
+
+		jabber_iq_send(iq);
+	}
+
 	/* If there are manually specified bytestream proxies, query them */
 	ft_proxies = purple_account_get_string(js->gc->account, "ft_proxies", NULL);
 	if (ft_proxies) {
@@ -574,6 +584,8 @@
 			js->server_caps |= JABBER_CAP_ADHOC;
 		} else if (!strcmp(NS_SIMPLE_BLOCKING, var)) {
 			js->server_caps |= JABBER_CAP_BLOCKING;
+		} else if(!strcmp(var, NS_XMPP_CARBONS)) {
+			js->server_caps |= JABBER_CAP_CARBONS;
 		}
 	}
 
diff -r c8e809dffa1d -r 1836d0d921db libpurple/protocols/jabber/jabber.c
--- a/libpurple/protocols/jabber/jabber.c	Thu Feb 21 23:07:08 2013 +0100
+++ b/libpurple/protocols/jabber/jabber.c	Thu May 23 21:47:32 2013 +0200
@@ -2566,6 +2566,7 @@
 	JabberStream *js = gc->proto_data;
 	GList *m = NULL;
 	PurplePluginAction *act;
+	gboolean has_carbons = purple_account_get_bool(gc->account, "carbons", FALSE);
 
 	act = purple_plugin_action_new(_("Set User Info..."),
 	                             jabber_setup_set_info);
@@ -2589,6 +2590,11 @@
 	if(js->commands)
 		jabber_adhoc_init_server_commands(js, &m);
 
+	if(js->server_caps & JABBER_CAP_CARBONS) {
+		act = purple_plugin_action_new((has_carbons ? _("Disable Message Copies") : _("Enable Message Copies")), jabber_toggle_carbons);
+		m = g_list_append(m, act);
+	}
+
 	return m;
 }
 
diff -r c8e809dffa1d -r 1836d0d921db libpurple/protocols/jabber/jabber.h
--- a/libpurple/protocols/jabber/jabber.h	Thu Feb 21 23:07:08 2013 +0100
+++ b/libpurple/protocols/jabber/jabber.h	Thu May 23 21:47:32 2013 +0200
@@ -48,6 +48,8 @@
 
 	JABBER_CAP_ITEMS          = 1 << 14,
 	JABBER_CAP_ROSTER_VERSIONING = 1 << 15,
+	
+	JABBER_CAP_CARBONS        = 1 << 16,
 
 	JABBER_CAP_RETRIEVED      = 1 << 31
 } JabberCapabilities;
diff -r c8e809dffa1d -r 1836d0d921db libpurple/protocols/jabber/message.c
--- a/libpurple/protocols/jabber/message.c	Thu Feb 21 23:07:08 2013 +0100
+++ b/libpurple/protocols/jabber/message.c	Thu May 23 21:47:32 2013 +0200
@@ -58,7 +58,8 @@
 
 static void handle_chat(JabberMessage *jm)
 {
-	JabberID *jid = jabber_id_new(jm->from);
+	gchar *contact = jm->outgoing ? jm->to : jm->from;
+	JabberID *jid = jabber_id_new(contact);
 
 	PurpleConnection *gc;
 	PurpleAccount *account;
@@ -71,48 +72,50 @@
 	gc = jm->js->gc;
 	account = purple_connection_get_account(gc);
 
-	jb = jabber_buddy_find(jm->js, jm->from, TRUE);
+	jb = jabber_buddy_find(jm->js, contact, TRUE);
 	jbr = jabber_buddy_find_resource(jb, jid->resource);
 
 	if(!jm->xhtml && !jm->body) {
-		if (jbr && jm->chat_state != JM_STATE_NONE)
-			jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
+		if (!jm->outgoing) {
+			if (jbr && jm->chat_state != JM_STATE_NONE)
+				jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
 
-		if(JM_STATE_COMPOSING == jm->chat_state) {
-			serv_got_typing(gc, jm->from, 0, PURPLE_TYPING);
-		} else if(JM_STATE_PAUSED == jm->chat_state) {
-			serv_got_typing(gc, jm->from, 0, PURPLE_TYPED);
-		} else if(JM_STATE_GONE == jm->chat_state) {
-			PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-					jm->from, account);
-			if (conv && jid->node && jid->domain) {
-				char buf[256];
-				PurpleBuddy *buddy;
+			if(JM_STATE_COMPOSING == jm->chat_state) {
+				serv_got_typing(gc, contact, 0, PURPLE_TYPING);
+			} else if(JM_STATE_PAUSED == jm->chat_state) {
+				serv_got_typing(gc, contact, 0, PURPLE_TYPED);
+			} else if(JM_STATE_GONE == jm->chat_state) {
+				PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+						contact, account);
+				if (conv && jid->node && jid->domain) {
+					char buf[256];
+					PurpleBuddy *buddy;
 
-				g_snprintf(buf, sizeof(buf), "%s@%s", jid->node, jid->domain);
+					g_snprintf(buf, sizeof(buf), "%s@%s", jid->node, jid->domain);
 
-				if ((buddy = purple_find_buddy(account, buf))) {
-					const char *who;
-					char *escaped;
+					if ((buddy = purple_find_buddy(account, buf))) {
+						const char *who;
+						char *escaped;
 
-					who = purple_buddy_get_alias(buddy);
-					escaped = g_markup_escape_text(who, -1);
+						who = purple_buddy_get_alias(buddy);
+						escaped = g_markup_escape_text(who, -1);
 
-					g_snprintf(buf, sizeof(buf),
-					           _("%s has left the conversation."), escaped);
-					g_free(escaped);
+						g_snprintf(buf, sizeof(buf),
+						           _("%s has left the conversation."), escaped);
+						g_free(escaped);
 
-					/* At some point when we restructure PurpleConversation,
-					 * this should be able to be implemented by removing the
-					 * user from the conversation like we do with chats now. */
-					purple_conversation_write(conv, "", buf,
-					                        PURPLE_MESSAGE_SYSTEM, time(NULL));
+						/* At some point when we restructure PurpleConversation,
+						 * this should be able to be implemented by removing the
+						 * user from the conversation like we do with chats now. */
+						purple_conversation_write(conv, "", buf,
+						                        PURPLE_MESSAGE_SYSTEM, time(NULL));
+					}
 				}
+				serv_got_typing_stopped(gc, contact);
+
+			} else {
+				serv_got_typing_stopped(gc, contact);
 			}
-			serv_got_typing_stopped(gc, jm->from);
-
-		} else {
-			serv_got_typing_stopped(gc, jm->from);
 		}
 	} else {
 		if (jid->resource) {
@@ -128,12 +131,12 @@
 			PurpleConversation *conv;
 
 			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-			                                             jm->from, account);
-			if (conv && !g_str_equal(jm->from,
+			                                             contact, account);
+			if (conv && !g_str_equal(contact,
 			                         purple_conversation_get_name(conv))) {
 				purple_debug_info("jabber", "Binding conversation to %s\n",
-				                  jm->from);
-				purple_conversation_set_name(conv, jm->from);
+				                  contact);
+				purple_conversation_set_name(conv, contact);
 			}
 		}
 
@@ -156,7 +159,8 @@
 			jm->body = jabber_google_format_to_html(jm->body);
 			g_free(tmp);
 		}
-		serv_got_im(gc, jm->from, jm->xhtml ? jm->xhtml : jm->body, 0, jm->sent);
+		serv_got_im(gc, contact, jm->xhtml ? jm->xhtml : jm->body,
+					(jm->outgoing ? PURPLE_MESSAGE_SEND : PURPLE_MESSAGE_RECV), jm->sent);
 	}
 
 	jabber_id_free(jid);
@@ -502,7 +506,50 @@
 	JabberMessage *jm;
 	const char *id, *from, *to, *type;
 	xmlnode *child;
-	gboolean signal_return;
+	gboolean signal_return, is_outgoing = FALSE;
+	time_t message_timestamp = time(NULL);
+	gboolean delayed = FALSE;
+
+	/* Check if this is a carbon-copy of a message.
+	 * If so, use that instead for the rest of this function,
+	 * but keep track of wether the from and to should be swapped.
+	 */
+	for(child = packet->child; child; child = child->next) {
+		const char *xmlns = xmlnode_get_namespace(child);
+
+		if (purple_strequal(child->name, "forwarded") && purple_strequal(xmlns, NS_XMPP_FORWARD)) {
+			xmlnode *subchild;
+
+			for(subchild = child->child; subchild; subchild = subchild->next) {
+				const char *sub_xmlns = xmlnode_get_namespace(subchild);
+
+				if (purple_strequal(subchild->name, "message")
+						&& purple_strequal(sub_xmlns, NS_XMPP_CLIENT)) {
+
+					/* This is the forwarded message, handle this instead */
+					purple_debug_info("jabber", "It's a carbon-copy, using the wrapped message instead.\n");
+					packet = subchild;
+				} else if(purple_strequal(subchild->name, "delay")
+							&& purple_strequal(sub_xmlns, NS_DELAYED_DELIVERY)) {
+
+					const char *timestamp = xmlnode_get_attrib(subchild, "stamp");
+					purple_debug_info("jabber", "Found a delay stamp: %s\n", timestamp);
+					delayed = TRUE;
+					if(timestamp)
+						message_timestamp = purple_str_to_time(timestamp, TRUE, NULL, NULL, NULL);
+				}
+			}
+		} else if (purple_strequal(child->name, "received")
+			&& purple_strequal(xmlns, NS_XMPP_CARBONS)) {
+
+			purple_debug_info("jabber", "It is an incoming message.\n");
+			is_outgoing = FALSE;
+		} else if (purple_strequal(child->name, "sent")
+					&& purple_strequal(xmlns, NS_XMPP_CARBONS)) {
+			purple_debug_info("jabber", "It is an outgoing message.\n");
+			is_outgoing = TRUE;
+		}
+    }
 
 	from = xmlnode_get_attrib(packet, "from");
 	id   = xmlnode_get_attrib(packet, "id");
@@ -516,9 +563,10 @@
 
 	jm = g_new0(JabberMessage, 1);
 	jm->js = js;
-	jm->sent = time(NULL);
-	jm->delayed = FALSE;
+	jm->sent = message_timestamp;
+	jm->delayed = delayed;
 	jm->chat_state = JM_STATE_NONE;
+	jm->outgoing = is_outgoing;
 
 	if(type) {
 		if(!strcmp(type, "normal"))
@@ -624,12 +672,12 @@
 						           jm->type == JABBER_MESSAGE_CHAT) {
 							conv =
 								purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
-									from, account);
+									is_outgoing ? to : from, account);
 							if (!conv) {
 								/* we need to create the conversation here */
 								conv =
 									purple_conversation_new(PURPLE_CONV_TYPE_IM,
-									account, from);
+									account, is_outgoing ? to : from);
 							}
 						}
 					}
@@ -711,6 +759,7 @@
 		} else if(!strcmp(child->name, "attention") && !strcmp(xmlns, NS_ATTENTION)) {
 			jm->hasBuzz = TRUE;
 		} else if(!strcmp(child->name, "delay") && !strcmp(xmlns, NS_DELAYED_DELIVERY)) {
+			/* Carbons/Stanza fowarding might have already set jm->delayed. However, this timestamp was certainly applied earlier, so it overrides Carbons. */
 			const char *timestamp = xmlnode_get_attrib(child, "stamp");
 			jm->delayed = TRUE;
 			if(timestamp)
@@ -1307,3 +1356,24 @@
 
 	return purple_account_get_bool(account, "custom_smileys", TRUE);
 }
+
+void jabber_toggle_carbons(PurplePluginAction *action) {
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
+	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
+	gboolean has_carbons = !purple_account_get_bool(purple_connection_get_account(gc), "carbons", FALSE);
+	xmlnode *node;
+
+	if(has_carbons) {
+		node = xmlnode_new_child(iq->node, "enable");
+	} else {
+		node = xmlnode_new_child(iq->node, "disable");
+	}
+
+	purple_account_set_bool(gc->account, "carbons", has_carbons);
+
+	xmlnode_set_namespace(node, NS_XMPP_CARBONS);
+	jabber_iq_send(iq);
+
+	purple_prpl_got_account_actions(purple_connection_get_account(gc));
+}
diff -r c8e809dffa1d -r 1836d0d921db libpurple/protocols/jabber/message.h
--- a/libpurple/protocols/jabber/message.h	Thu Feb 21 23:07:08 2013 +0100
+++ b/libpurple/protocols/jabber/message.h	Thu May 23 21:47:32 2013 +0200
@@ -62,6 +62,7 @@
 	} chat_state;
 	GList *etc;
 	GList *eventitems;
+	gboolean outgoing;
 } JabberMessage;
 
 void jabber_message_free(JabberMessage *jm);
@@ -79,4 +80,6 @@
 
 gboolean jabber_custom_smileys_isenabled(JabberStream *js, const const gchar *namespace);
 
+void jabber_toggle_carbons(PurplePluginAction *action);
+
 #endif /* PURPLE_JABBER_MESSAGE_H_ */
diff -r c8e809dffa1d -r 1836d0d921db libpurple/protocols/jabber/namespaces.h
--- a/libpurple/protocols/jabber/namespaces.h	Thu Feb 21 23:07:08 2013 +0100
+++ b/libpurple/protocols/jabber/namespaces.h	Thu May 23 21:47:32 2013 +0200
@@ -95,6 +95,12 @@
 /* XEP-0264 File Transfer Thumbnails (Thumbs) */
 #define NS_THUMBS "urn:xmpp:thumbs:0"
 
+/* XEP-0280 Message Carbons */
+#define NS_XMPP_CARBONS "urn:xmpp:carbons:1"
+
+/* XEP-0297 Message Forwarding */
+#define NS_XMPP_FORWARD "urn:xmpp:forward:0"
+
 /* Google extensions */
 #define NS_GOOGLE_CAMERA "http://www.google.com/xmpp/protocol/camera/v1"
 #define NS_GOOGLE_VIDEO "http://www.google.com/xmpp/protocol/video/v1"




More information about the commits mailing list