Software /
code /
verse
File
plugins/jingle_ibb.lua @ 498:50d0bd035bb7
util.sasl.oauthbearer: Don't send authzid
It's not needed and not recommended in XMPP unless we want to act as
someone other than who we authenticate as. We find out the JID during
resource binding.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 23 Jun 2023 12:09:49 +0200 |
parent | 490:6b2f31da9610 |
line wrap: on
line source
local verse = require "verse"; local base64 = require "prosody.util.encodings".base64; local new_id = require "prosody.util.id".short; 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 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 = new_id(); 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