# HG changeset patch # User Matthew Wild # Date 1298418660 0 # Node ID 4678932455a33f8c223f4e00d2abd1b7ba1bba27 # Parent 7ad218bd5fde5d4ca2372ba5d5e8b8359e2d7571 plugins.smacks: XEP-0198 support diff -r 7ad218bd5fde -r 4678932455a3 plugins/smacks.lua --- /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