Software /
code /
verse
File
plugins/jingle_ibb.lua @ 445:b119dc4d8bc2
plugins.smacks: Don't warn about zero stanzas acked
It's only if the count somehow goes backwards that something is really
wrong. An ack for zero stanzas is fine and we don't need to do anything.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 10 Jun 2021 11:58:23 +0200 |
parent | 428:bde804b01f28 |
child | 457:73d4eb93657b |
line wrap: on
line source
local verse = require "verse"; local base64 = require "util.encodings".base64; local uuid_generate = require "util.uuid".generate; local xmlns_jingle_ibb = "urn:xmpp:jingle:transports:ibb:1"; local xmlns_ibb = "http://jabber.org/protocol/ibb"; assert(base64.encode("This is a test.") == "VGhpcyBpcyBhIHRlc3Qu", "Base64 encoding failed"); assert(base64.decode("VGhpcyBpcyBhIHRlc3Qu") == "This is a test.", "Base64 decoding failed"); local t_concat = table.concat local ibb_conn = {}; local ibb_conn_mt = { __index = ibb_conn }; local function new_ibb(stream) local conn = setmetatable({ stream = stream }, ibb_conn_mt) conn = verse.eventable(conn); return conn; end function ibb_conn:initiate(peer, sid, stanza) self.block = 2048; -- ignored for now self.stanza = stanza or 'iq'; self.peer = peer; self.sid = sid or tostring(self):match("%x+$"); self.iseq = 0; self.oseq = 0; local feeder = function(stanza) return self:feed(stanza) end self.feeder = feeder; print("Hooking incoming IQs"); local stream = self.stream; stream:hook("iq/".. xmlns_ibb, feeder) if stanza == "message" then stream:hook("message", feeder) end end function ibb_conn:open(callback) self.stream:send_iq(verse.iq{ to = self.peer, type = "set" } :tag("open", { xmlns = xmlns_ibb, ["block-size"] = self.block, sid = self.sid, stanza = self.stanza }) , function(reply) if callback then if reply.attr.type ~= "error" then callback(true) else callback(false, reply:get_error()) end end end); end function ibb_conn:send(data) local stanza = self.stanza; local st; if stanza == "iq" then st = verse.iq{ type = "set", to = self.peer } elseif stanza == "message" then st = verse.message{ to = self.peer } end local seq = self.oseq; self.oseq = seq + 1; st:tag("data", { xmlns = xmlns_ibb, sid = self.sid, seq = seq }) :text(base64.encode(data)); if stanza == "iq" then self.stream:send_iq(st, function(reply) self:event(reply.attr.type == "result" and "drained" or "error"); end) else stream:send(st) self:event("drained"); end end function ibb_conn:feed(stanza) if stanza.attr.from ~= self.peer then return end local child = stanza[1]; if child.attr.sid ~= self.sid then return end local ok; if child.name == "open" then self:event("connected"); self.stream:send(verse.reply(stanza)) return true elseif child.name == "data" then local bdata = stanza:get_child_text("data", xmlns_ibb); local seq = tonumber(child.attr.seq); local expected_seq = self.iseq; if bdata and seq then if seq ~= expected_seq then self.stream:send(verse.error_reply(stanza, "cancel", "not-acceptable", "Wrong sequence. Packet lost?")) self:close(); self:event("error"); return true; end self.iseq = seq + 1; local data = base64.decode(bdata); if self.stanza == "iq" then self.stream:send(verse.reply(stanza)) end self:event("incoming-raw", data); return true; end elseif child.name == "close" then self.stream:send(verse.reply(stanza)) self:close(); return true end end --[[ FIXME some day function ibb_conn:receive(patt) -- is this even used? print("ibb_conn:receive("..tostring(patt)..")"); assert(patt == "*a" or tonumber(patt)); local data = t_concat(self.ibuffer):sub(self.pos, tonumber(patt) or nil); self.pos = self.pos + #data; return data end function ibb_conn:dirty() print("ibb_conn:dirty()"); return false -- ???? end function ibb_conn:getfd() return 0 end function ibb_conn:settimeout(n) -- ignore? end -]] function ibb_conn:close() self.stream:unhook("iq/".. xmlns_ibb, self.feeder) self:event("disconnected"); end function verse.plugins.jingle_ibb(stream) stream:hook("ready", function () stream:add_disco_feature(xmlns_jingle_ibb); end, 10); local ibb = {}; function ibb:_setup() local conn = new_ibb(self.stream); conn.sid = self.sid or conn.sid; conn.stanza = self.stanza or conn.stanza; conn.block = self.block or conn.block; conn:initiate(self.peer, self.sid, self.stanza); self.conn = conn; end function ibb:generate_initiate() print("ibb:generate_initiate() as ".. self.role); local sid = uuid_generate(); self.sid = sid; self.stanza = 'iq'; self.block = 2048; local transport = verse.stanza("transport", { xmlns = xmlns_jingle_ibb, sid = self.sid, stanza = self.stanza, ["block-size"] = self.block }); return transport; end function ibb:generate_accept(initiate_transport) print("ibb:generate_accept() as ".. self.role); local attr = initiate_transport.attr; self.sid = attr.sid or self.sid; self.stanza = attr.stanza or self.stanza; self.block = attr["block-size"] or self.block; self:_setup(); return initiate_transport; end function ibb:connect(callback) if not self.conn then self:_setup(); end local conn = self.conn; print("ibb:connect() as ".. self.role); if self.role == "initiator" then conn:open(function(ok, ...) assert(ok, table.concat({...}, ", ")); callback(conn); end); else callback(conn); end end function ibb:info_received(jingle_tag) print("ibb:info_received()"); -- TODO, what exactly? end function ibb:disconnect() if self.conn then self.conn:close() end end function ibb:handle_accepted(jingle_tag) end local ibb_mt = { __index = ibb }; stream:hook("jingle/transport/"..xmlns_jingle_ibb, function (jingle) return setmetatable({ role = jingle.role, peer = jingle.peer, stream = jingle.stream, jingle = jingle, }, ibb_mt); end); end