diff --git a/resources/prosody-plugins/mod_auth_token.lua b/resources/prosody-plugins/mod_auth_token.lua index d83f0b7bb..94b065eaf 100644 --- a/resources/prosody-plugins/mod_auth_token.lua +++ b/resources/prosody-plugins/mod_auth_token.lua @@ -23,8 +23,12 @@ module:hook("bosh-session", function(event) local query = request.url.query; if query ~= nil then - session.auth_token = query and formdecode(query).token or nil; - end + local params = formdecode(query); + session.auth_token = query and params.token or nil; + + -- The room name from the bosh query + session.jitsi_bosh_query_room = params.room; + end end); function provider.test_password(username, password) @@ -54,7 +58,18 @@ end function provider.get_sasl_handler(session) local function get_username_from_token(self, message) - return token_util:process_and_verify_token(session); + local res = token_util:process_and_verify_token(session); + + local customUsername + = prosody.events.fire_event("pre-jitsi-authentication", session); + + if (customUsername) then + self.username = customUsername; + else + self.username = message; + end + + return res; end return new_sasl(host, { anonymous = get_username_from_token }); @@ -69,8 +84,6 @@ local function anonymous(self, message) -- This calls the handler created in 'provider.get_sasl_handler(session)' local result, err, msg = self.profile.anonymous(self, username, self.realm); - self.username = username; - if result == true then return "success"; else diff --git a/resources/prosody-plugins/mod_muc_poltergeist.lua b/resources/prosody-plugins/mod_muc_poltergeist.lua new file mode 100644 index 000000000..8e8db4e53 --- /dev/null +++ b/resources/prosody-plugins/mod_muc_poltergeist.lua @@ -0,0 +1,240 @@ +local bare = require "util.jid".bare; +local generate_uuid = require "util.uuid".generate; +local jid = require "util.jid"; +local neturl = require "net.url"; +local parse = neturl.parseQuery; +local st = require "util.stanza"; +local get_room_from_jid = module:require "util".get_room_from_jid; + +-- Options +local poltergeist_component + = module:get_option_string("poltergeist_component", module.host); + +-- table to store all poltergeists we create +local poltergeists = {}; + +-- poltergaist management functions + +-- Returns the room if available, work and in multidomain mode +-- @param room_name the name of the room +-- @param group name of the group (optional) +-- @return returns room if found or nil +function get_room(room_name, group) + local room_address = jid.join(room_name, module:get_host()); + -- if there is a group we are in multidomain mode + if group and group ~= "" then + room_address = "["..group.."]"..room_address; + end + + return get_room_from_jid(room_address); +end + +-- Stores the username in the table where we store poltergeist usernames +-- based on their room names +-- @param room the room instance +-- @param user_id the user id +-- @param username the username to store +function store_username(room, user_id, username) + local room_name = jid.node(room.jid); + + -- we store in poltergeist user ids for room names + if (not poltergeists[room_name]) then + poltergeists[room_name] = {}; + end + poltergeists[room_name][user_id] = username; + log("debug", "stored in session: %s", username); +end + +-- Retrieve the username for a user +-- @param room the room instance +-- @param user_id the user id +-- @return returns the stored username for user or nil +function get_username(room, user_id) + local room_name = jid.node(room.jid); + + if (not poltergeists[room_name]) then + return nil; + end + + return poltergeists[room_name][user_id]; +end + +-- if we found that a session for a user with id has a poltergiest already +-- created, retrieve its jid and return it to the authentication +-- so we can reuse it and we that real user will replace the poltergiest +prosody.events.add_handler("pre-jitsi-authentication", function(session) + + if (session.jitsi_meet_context_user) then + local room = get_room( + session.jitsi_bosh_query_room, + session.jitsi_meet_context_group); + + if (not room) then + return nil; + end + + local username + = get_username(room, session.jitsi_meet_context_user["id"]); + + if (not username) then + return nil; + end + + log("debug", "Found predefined username %s", username); + + -- let's find the room and if the poltergeist occupant is there + -- lets remove him before the real participant joins + -- when we see the unavailable presence to go out the server + -- we will mark it with ignore tag + local nick = string.sub(username, 0, 8); + if (have_poltergeist_occupant(room, nick)) then + remove_poltergeist_occupant(room, nick); + end + + return username; + end + + return nil; +end); + +-- Creates poltergeist occupant +-- @param room the room instance where we create the occupant +-- @param nick the nick to use for the new occupant +-- @param name the display name fot the occupant (optional) +-- @param avatar the avatar to use for the new occupant (optional) +function create_poltergeist_occupant(room, nick, name, avatar) + log("debug", "create_poltergeist_occupant %s:", nick); + -- Join poltergeist occupant to room, with the invited JID as their nick + local join_presence = st.presence({ + to = room.jid.."/"..nick, + from = poltergeist_component.."/"..nick + }):tag("x", { xmlns = "http://jabber.org/protocol/muc" }):up(); + + if (name) then + join_presence:tag( + "nick", + { xmlns = "http://jabber.org/protocol/nick" }):text(name):up(); + end + if (avatar) then + join_presence:tag("avatar-url"):text(avatar):up(); + end + + room:handle_first_presence( + prosody.hosts[poltergeist_component], join_presence); +end + +-- Removes poltergeist occupant +-- @param room the room instance where to remove the occupant +-- @param nick the nick of the occupant to remove +function remove_poltergeist_occupant(room, nick) + log("debug", "remove_poltergeist_occupant %s", nick); + local leave_presence = st.presence({ + to = room.jid.."/"..nick, + from = poltergeist_component.."/"..nick, + type = "unavailable" }); + room:handle_normal_presence( + prosody.hosts[poltergeist_component], leave_presence); +end + +-- Checks for existance of a poltergeist occupant +-- @param room the room instance where to check for occupant +-- @param nick the nick of the occupant +-- @return true if occupant is found, false otherwise +function have_poltergeist_occupant(room, nick) + -- Find out if we have a poltergeist occupant in the room for this JID + return not not room:get_occupant_jid(poltergeist_component.."/"..nick); +end + +-- Event handlers + +--- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100, +--- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100. +module:hook("muc-decline", function (event) + remove_poltergeist_occupant(event.room, bare(event.stanza.attr.from)); +end, -100); +-- before sending the presence for a poltergeist leaving add ignore tag +-- as poltergeist is leaving just before the real user joins and in the client +-- we ignore this presence to avoid leaving/joining experience and the real +-- user will reuse all currently created UI components for the same nick +module:hook("muc-broadcast-presence", function (event) + if (bare(event.occupant.jid) == poltergeist_component) then + if(event.stanza.attr.type == "unavailable") then + event.stanza:tag( + "ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up(); + end + end +end, -100); + +--- Handles request for creating/managing poltergeists +-- @param event the http event, holds the request query +-- @return GET response, containing a json with response details +function handle_create_poltergeist (event) + local params = parse(event.request.url.query); + local user_id = params["user"]; + local room_name = params["room"]; + local group = params["group"]; + local name = params["name"]; + local avatar = params["avatar"]; + + local room = get_room(room_name, group); + if (not room) then + log("error", "no room found %s", room_address); + return 404; + end + + local username = generate_uuid(); + store_username(room, user_id, username) + + create_poltergeist_occupant(room, string.sub(username,0,8), name, avatar); + + return 200; +end + +--- Handles request for updating poltergeists status +-- @param event the http event, holds the request query +-- @return GET response, containing a json with response details +function handle_update_poltergeist (event) + local params = parse(event.request.url.query); + local user_id = params["user"]; + local room_name = params["room"]; + local group = params["group"]; + local status = params["status"]; + + local room = get_room(room_name, group); + if (not room) then + log("error", "no room found %s", room_address); + return 404; + end + + local username = get_username(room, user_id); + if (not username) then + return 404; + end + + local nick = string.sub(username, 0, 8); + if (have_poltergeist_occupant(room, nick)) then + local update_presence = st.presence({ + to = room.jid.."/"..nick, + from = poltergeist_component.."/"..nick + }):tag("status"):text(status):up(); + + room:handle_normal_presence( + prosody.hosts[poltergeist_component], update_presence); + + return 200; + else + return 404; + end +end + + +log("info", "Loading poltergeist service"); +module:depends("http"); +module:provides("http", { + default_path = "/"; + name = "poltergeist"; + route = { + ["GET /poltergeist/create"] = handle_create_poltergeist; + ["GET /poltergeist/update"] = handle_update_poltergeist; + }; +}); diff --git a/resources/prosody-plugins/mod_muc_size.lua b/resources/prosody-plugins/mod_muc_size.lua index dfc9c7a65..d8d2716c5 100644 --- a/resources/prosody-plugins/mod_muc_size.lua +++ b/resources/prosody-plugins/mod_muc_size.lua @@ -19,6 +19,7 @@ local enableTokenVerification = module:get_option_boolean("enable_roomsize_token_verification", false); local token_util = module:require "token/util".new(module); +local get_room_from_jid = module:require "util".get_room_from_jid; -- no token configuration but required if token_util == nil and enableTokenVerification then @@ -31,26 +32,6 @@ end local muc_domain_prefix = module:get_option_string("muc_mapper_domain_prefix", "conference"); ---- Finds and returns room by its jid --- @param room_jid the room jid to search in the muc component --- @return returns room if found or nil -function get_room_from_jid(room_jid) - local _, host = jid.split(room_jid); - local component = hosts[host]; - if component then - local muc = component.modules.muc - if muc and rawget(muc,"rooms") then - -- We're running 0.9.x or 0.10 (old MUC API) - return muc.rooms[room_jid]; - elseif muc and rawget(muc,"get_room_from_jid") then - -- We're running >0.10 (new MUC API) - return muc.get_room_from_jid(room_jid); - else - return - end - end -end - --- Verifies room name, domain name with the values in the token -- @param token the token we received -- @param room_address the full room address jid diff --git a/resources/prosody-plugins/mod_poltergeist_component.lua b/resources/prosody-plugins/mod_poltergeist_component.lua new file mode 100644 index 000000000..aa06175db --- /dev/null +++ b/resources/prosody-plugins/mod_poltergeist_component.lua @@ -0,0 +1,21 @@ +local st = require "util.stanza"; + +-- A component which we use to receive all stanzas for the created poltergeists +-- replays with error if an iq is sent +function no_action() + return true; +end + +function error_reply(event) + module:send(st.error_reply(event.stanza, "cancel", "service-unavailable")); + return true; +end + +module:hook("presence/host", no_action); +module:hook("message/host", no_action); +module:hook("presence/full", no_action); +module:hook("message/full", no_action); + +module:hook("iq/host", error_reply); +module:hook("iq/full", error_reply); +module:hook("iq/bare", error_reply); diff --git a/resources/prosody-plugins/util.lib.lua b/resources/prosody-plugins/util.lib.lua new file mode 100644 index 000000000..b0a6a38f0 --- /dev/null +++ b/resources/prosody-plugins/util.lib.lua @@ -0,0 +1,25 @@ +local jid = require "util.jid"; + +--- Finds and returns room by its jid +-- @param room_jid the room jid to search in the muc component +-- @return returns room if found or nil +function get_room_from_jid(room_jid) + local _, host = jid.split(room_jid); + local component = hosts[host]; + if component then + local muc = component.modules.muc + if muc and rawget(muc,"rooms") then + -- We're running 0.9.x or 0.10 (old MUC API) + return muc.rooms[room_jid]; + elseif muc and rawget(muc,"get_room_from_jid") then + -- We're running >0.10 (new MUC API) + return muc.get_room_from_jid(room_jid); + else + return + end + end +end + +return { + get_room_from_jid = get_room_from_jid; +}; \ No newline at end of file