Diff

plugins/smacks.lua @ 188:4678932455a3

plugins.smacks: XEP-0198 support
author Matthew Wild <mwild1@gmail.com>
date Tue, 22 Feb 2011 23:51:00 +0000
child 197:7e98cf2c1d8d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/smacks.lua	Tue Feb 22 23:51:00 2011 +0000
@@ -0,0 +1,98 @@
+local st = require "util.stanza";
+
+local xmlns_sm = "urn:xmpp:sm:2";
+
+function verse.plugins.smacks(stream)
+	-- State for outgoing stanzas
+	local outgoing_queue = {};
+	local last_ack = 0;
+	
+	-- State for incoming stanzas
+	local handled_stanza_count = 0;
+	
+	-- Catch incoming stanzas
+	local function incoming_stanza(stanza)
+		if stanza.attr.xmlns == "jabber:client" or not stanza.attr.xmlns then
+			handled_stanza_count = handled_stanza_count + 1;
+			stream:debug("Increasing handled stanzas to %d for %s", handled_stanza_count, stanza:top_tag());
+		end
+	end
+	
+	local function on_disconnect()
+		stream.stream_management_supported = nil;
+		if stream.resumption_token then
+			stream.authenticated = nil;
+			stream:connect(stream.connect_host or stream.host, stream.connect_port or 5222);
+			stream:reopen();
+			return true;
+		end
+	end	
+	
+	local function handle_sm_command(stanza)
+		if stanza.name == "r" then -- Request for acks for stanzas we received
+			stream:send(verse.stanza("a", { xmlns = xmlns_sm, h = tostring(handled_stanza_count) }));
+		elseif stanza.name == "a" then -- Ack for stanzas we sent
+			local new_ack = tonumber(stanza.attr.h);
+			if new_ack > last_ack then
+				local old_unacked = #outgoing_queue;
+				for i=last_ack+1,new_ack do
+					table.remove(outgoing_queue, 1);
+				end
+				stream:debug("Received ack: New ack: "..new_ack.." Last ack: "..last_ack.." Unacked stanzas now: "..#outgoing_queue.." (was "..old_unacked..")");
+				last_ack = new_ack;
+			else
+				stream:warn("Received bad ack for "..new_ack.." when last ack was "..last_ack);
+			end
+		elseif stanza.name == "enabled" then
+			stream.smacks = true;
+			-- Catch outgoing stanzas
+			local old_send = stream.send;
+			function stream.send(stream, stanza)
+				stream:warn("SENDING");
+				if not stanza.attr.xmlns then
+					outgoing_queue[#outgoing_queue+1] = stanza;
+					local ret = old_send(stream, stanza);
+					old_send(stream, verse.stanza("r", { xmlns = xmlns_sm }));
+					return ret;
+				end
+				return old_send(stream, stanza);
+			end
+			-- Catch incoming stanzas
+			stream:hook("stanza", incoming_stanza);
+			
+			if stanza.attr.id then
+				stream.resumption_token = stanza.attr.id;
+				stream:hook("disconnected", on_disconnect, 100);
+			end
+		elseif stanza.name == "resumed" then
+			stream:debug("Resumed successfully");
+			stream:send(verse.message{to="me@matthewwild.co.uk", type="chat"}:tag("body"):text("Hi again!"));
+		else
+			stream:warn("Don't know how to handle "..xmlns_sm.."/"..stanza.name);
+		end
+	end
+
+	local function on_bind_success()
+		if not stream.smacks then
+			--stream:unhook("bind-success", on_bind_success);
+			stream:send(st.stanza("enable", { xmlns = xmlns_sm, resume = "true" }));
+		end
+	end
+
+	local function on_features(features)
+		if features:get_child("sm", xmlns_sm) then
+			stream.stream_management_supported = true;
+			if stream.smacks and stream.bound then -- Already enabled in a previous session - resume
+				stream:send(st.stanza("resume", { xmlns = xmlns_sm,
+					h = handled_stanza_count, previd = stream.resumption_token }));
+			else
+				stream:hook("bind-success", on_bind_success);
+			end
+			return true;
+		end
+	end
+
+	stream:hook("stream-features", on_features, 150);
+	stream:hook("stream/"..xmlns_sm, handle_sm_command);
+	--stream:hook("ready", on_stream_ready, 500);
+end