diff --git a/mod_mix_pam/mod_mix_pam.lua b/mod_mix_pam/mod_mix_pam.lua index 5b3084a..8d78ea8 100644 --- a/mod_mix_pam/mod_mix_pam.lua +++ b/mod_mix_pam/mod_mix_pam.lua @@ -1,23 +1,58 @@ -if module:get_host() ~= "*" then +local host = module:get_host() +--[[ +if host ~= "*" then module:log("error", "mix_pam should be used on the user host!"); end +]]-- local jid = require("util.jid"); local st = require("util.stanza"); +local rm_remove_from_roster = require("core.rostermanager").remove_from_roster; +local rm_add_to_roster = require("core.rostermanager").add_to_roster; +local rm_roster_push = require("core.rostermanager").roster_push; -- Persistent storage local mix_pam = module:open_store("mix_pam", "keyval"); -- Runtime data local mix_hosts = {}; +local mix_spids = {}; -- user -> channel -> spid -- Namespaceing local mix_pam_xmlns = "urn:xmpp:mix:pam:2"; +local mix_roster_xmlns = "urn:xmpp:mix:roster:0"; module:add_feature(mix_pam_xmlns); -- NOTE: To show that we archive messages -- module:add_feature(mix_pam_xmlns.."#archive"); +function contains_key(table, key) + -- Returns true if table contains the key key. Returns false + -- otherwise. + return table[key] ~= nil; +end + +function set_mix_spid(user, channel, spid) + if mix_spids[user] ~= nil then + mix_spids[user][channel] = spid; + else + mix_spids[user] = { [channel] = spid }; + end + + mix_pam:set("spids", mix_spids); +end +function get_mix_spid(user, channel) + return mix_spids[user][channel]; +end; +function remove_mix_spid(user, channel) + mix_spids[user][channel] = nil; + if mix_spids[user][channel] == {} then + mix_spids[user][channel] = nil; + end + + mix_pam:set("spids", mix_spids); +end + function add_mix_host(host) if mix_hosts[host] ~= nil then mix_hosts[host] = mix_hosts[host] + 1; @@ -26,6 +61,8 @@ function add_mix_host(host) module:log("debug", "Added %s as a new MIX host", host); mix_hosts[host] = 1; end + + mix_pam:set("hosts", mix_hosts); end function remove_mix_host(host) @@ -41,6 +78,8 @@ function remove_mix_host(host) else module:log("debug", "Attempt to remove unknown MIX host"); end + + mix_pam:set("hosts", mix_hosts); end function is_mix_host(host) @@ -58,11 +97,28 @@ function module.load() if mix_hosts == nil then module:log("info", "No known MIX hosts loaded"); mix_hosts = {}; - return; end - for _, host in pairs(mix_hosts) do + for host, _ in pairs(mix_hosts) do module:log("debug", "Known host: %s", host); end + + mix_spids = mix_pam:get("spids"); + module:log("info", "Loaded known MIX SPIDs"); + + if mix_spids == nil then + module:log("info", "No known MIX SPIDs loaded"); + mix_spids = {}; + end + for user, channel_map in pairs(mix_spids) do + module:log("debug", "-- %s", user); + if channel_map[user] then + for channel, spid in pairs(channel_map[user]) do + module:log("debug", "%s -> %s", channel, spid); + end + else + module:log("warn", "User mapping empty"); + end + end end local client_state_tracker = {}; -- [stanza ID] -> resource @@ -117,7 +173,7 @@ function handle_client_join(event) local join_iq = st.iq({ type = "set", from = jid.bare(stanza.attr.from), to = client_join.attr.channel, id = stanza.attr.id, xmlns = "jabber:client" }); join_iq:add_child(join); - add_state(stanza.attr.id, jid.resource(stanza.attr.from)); + add_state(stanza.attr.id, origin); module:send(join_iq); return true; @@ -144,7 +200,7 @@ function handle_client_leave(event) local leave_iq = st.iq({ type = "set", from = jid.bare(stanza.attr.from), to = client_leave.attr.channel, id = stanza.attr.id }); leave_iq:add_child(leave); - add_state(stanza.attr.id, jid.resource(stanza.attr.from)); + add_state(stanza.attr.id, origin); module:send(leave_iq); return true; @@ -168,44 +224,66 @@ end); function handle_mix_join(event) -- The MIX server responded - -- TODO: Do stuff to the user's roster module:log("debug", "Received MIX-JOIN result"); + module:log("debug", "Adding %s as a new MIX host", jid.host(event.stanza.attr.from)); add_mix_host(jid.host(event.stanza.attr.from)); mix_pam:set("hosts", mix_hosts); local stanza = event.stanza; - local channel_jid = stanza:get_child("join", "urn:xmpp:mix:core:1").attr.id.."#"..stanza.attr.from; - local resource = pop_state(stanza.attr.id); + local spid = stanza:get_child("join", "urn:xmpp:mix:core:1").attr.id; + local channel_jid = spid.."#"..stanza.attr.from; + local origin = pop_state(stanza.attr.id); + local resource = origin.resource; if resource == nil then module:log("error", "Got a MIX join result for a not-requested id %s. Maybe the MIX server changed the stanza ID?", stanza.attr.id); return false; end + + -- Keep track of the SPID + set_mix_spid(stanza.attr.to, stanza.attr.from, spid); local client_join = st.iq({ type = "result", id = stanza.attr.id, from = jid.bare(stanza.attr.to), to = stanza.attr.to.."/"..resource }) :tag("client-join", { xmlns = mix_pam_xmlns, jid = channel_jid }); client_join:add_child(stanza:get_child("join", "urn:xmpp:mix:core:1")); - module:send(client_join); + module:send(client_join); + + -- TODO: Error handling? + rm_add_to_roster(origin, stanza.attr.from, { + subscription = "both", + groups = {}, + }); + rm_roster_push(jid.node(stanza.attr.to), host, stanza.attr.from); + return true; end function handle_mix_leave(event) -- The MIX server responded - -- TODO: Do stuff to the user's roster module:log("debug", "Received MIX-LEAVE result"); remove_mix_host(jid.host(event.stanza.attr.from)); mix_pam:set("hosts", mix_hosts); local stanza = event.stanza; - local resource = pop_state(stanza.attr.id); + local origin = pop_state(stanza.attr.id); + local resource = origin.resource; if resource == nil then module:log("error", "Got a MIX leave result for a not-requested id %s. Maybe the MIX server changed the stanza ID?", stanza.attr.id); return false; end + -- Keep track of the SPID + set_mix_spid(stanza.attr.to, jid.bare(stanza.attr.from)); + local client_leave = st.iq({ type = "result", id = stanza.attr.id, from = jid.bare(stanza.attr.to), to = stanza.attr.to.."/"..resource }) :tag("client-leave", { xmlns = mix_pam_xmlns }); client_leave:add_child(stanza:get_child("leave", "urn:xmpp:mix:core:1")); module:send(client_leave); + + -- Remove from roster + -- TODO: Error handling + rm_remove_from_roster(origin, jid.bare(stanza.attr.from)); + rm_roster_push(jid.node(stanza.attr.to), host, jid.bare(stanza.attr.from)); + return true; end @@ -238,6 +316,29 @@ module:hook("resource-unbind", function(event) module:log("debug", "Unbind of not recorded resource %s (%s)", resource, jid); end); +module:hook("roster-get", function(event) + -- NOTE: Currently this requires a patch to make mod_roster emit + -- the roster-get event + local annotate = event.stanza + :get_child("query", "jabber:iq:roster") + :get_child("annotate", mix_roster_xmlns); + if not annotate then return; end + local reply, stanza = event.reply, event.stanza; + + module:log("debug", "Annotated roster request received"); + + -- User requested the roster with an + for item in event.reply:get_child("query", "jabber:iq:roster"):children() do + if contains_key(mix_hosts, jid.host(item.attr.jid)) then + local spid = get_mix_spid(jid.bare(stanza.attr.from), item.attr.jid); + item:tag("channel", { + xmlns = mix_roster_xmlns, + ["participant-id"] = spid, + }); + end + end +end); + module:hook("message/bare", function(event) local stanza = event.stanza; local host = jid.host(stanza.attr.from);