Merge pull request #1797 from jitsi/module-poltergeist_update
Module poltergeist update
This commit is contained in:
commit
c369330054
|
@ -5,13 +5,44 @@ 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;
|
||||
local wrap_async_run = module:require "util".wrap_async_run;
|
||||
local timer = require "util.timer";
|
||||
|
||||
-- Options
|
||||
local poltergeist_component
|
||||
= module:get_option_string("poltergeist_component", module.host);
|
||||
-- defaults to 3 min
|
||||
local poltergeist_timeout
|
||||
= module:get_option_string("poltergeist_leave_timeout", 180);
|
||||
-- this basically strips the domain from the conference.domain address
|
||||
local parentHostName = string.gmatch(tostring(module.host), "%w+.(%w.+)")();
|
||||
if parentHostName == nil then
|
||||
log("error", "Failed to start - unable to get parent hostname");
|
||||
return;
|
||||
end
|
||||
|
||||
local parentCtx = module:context(parentHostName);
|
||||
if parentCtx == nil then
|
||||
log("error",
|
||||
"Failed to start - unable to get parent context for host: %s",
|
||||
tostring(parentHostName));
|
||||
return;
|
||||
end
|
||||
local token_util = module:require "token/util".new(parentCtx);
|
||||
|
||||
-- option to enable/disable token verifications
|
||||
local disableTokenVerification
|
||||
= module:get_option_boolean("disable_polergeist_token_verification", false);
|
||||
|
||||
-- option to expire poltergeist with custom status text
|
||||
local poltergeistExpiredStatus
|
||||
= module:get_option_string("poltergeist_expired_status");
|
||||
|
||||
-- table to store all poltergeists we create
|
||||
local poltergeists = {};
|
||||
-- table to mark that outgoing unavailable presences
|
||||
-- should be marked with ignore
|
||||
local poltergeists_pr_ignore = {};
|
||||
|
||||
-- poltergaist management functions
|
||||
|
||||
|
@ -21,8 +52,9 @@ local poltergeists = {};
|
|||
-- @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
|
||||
-- if there is a group we are in multidomain mode and that group is not
|
||||
-- our parent host
|
||||
if group and group ~= "" and group ~= parentHostName then
|
||||
room_address = "["..group.."]"..room_address;
|
||||
end
|
||||
|
||||
|
@ -59,6 +91,67 @@ function get_username(room, user_id)
|
|||
return poltergeists[room_name][user_id];
|
||||
end
|
||||
|
||||
-- Removes poltergeist values from table
|
||||
-- @param room the room instance
|
||||
-- @param nick the user nick
|
||||
function remove_username(room, nick)
|
||||
local room_name = jid.node(room.jid);
|
||||
if (poltergeists[room_name]) then
|
||||
local user_id_to_remove;
|
||||
for name,username in pairs(poltergeists[room_name]) do
|
||||
if (string.sub(username, 0, 8) == nick) then
|
||||
user_id_to_remove = name;
|
||||
end
|
||||
end
|
||||
if (user_id_to_remove) then
|
||||
poltergeists[room_name][user_id_to_remove] = nil;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Verifies room name, domain name with the values in the token
|
||||
-- @param token the token we received
|
||||
-- @param room_name the room name
|
||||
-- @param group name of the group (optional)
|
||||
-- @return true if values are ok or false otherwise
|
||||
function verify_token(token, room_name, group)
|
||||
if disableTokenVerification then
|
||||
return true;
|
||||
end
|
||||
|
||||
-- if not disableTokenVerification and we do not have token
|
||||
-- stop here, cause the main virtual host can have guest access enabled
|
||||
-- (allowEmptyToken = true) and we will allow access to rooms info without
|
||||
-- a token
|
||||
if token == nil then
|
||||
log("warn", "no token provided");
|
||||
return false;
|
||||
end
|
||||
|
||||
local session = {};
|
||||
session.auth_token = token;
|
||||
local verified, reason = token_util:process_and_verify_token(session);
|
||||
if not verified then
|
||||
log("warn", "not a valid token %s", tostring(reason));
|
||||
return false;
|
||||
end
|
||||
|
||||
local room_address = jid.join(room_name, module:get_host());
|
||||
-- if there is a group we are in multidomain mode and that group is not
|
||||
-- our parent host
|
||||
if group and group ~= "" and group ~= parentHostName then
|
||||
room_address = "["..group.."]"..room_address;
|
||||
end
|
||||
|
||||
if not token_util:verify_room(session, room_address) then
|
||||
log("warn", "Token %s not allowed to join: %s",
|
||||
tostring(token), tostring(room_address));
|
||||
return false;
|
||||
end
|
||||
|
||||
return true;
|
||||
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
|
||||
|
@ -67,7 +160,7 @@ 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);
|
||||
session.jitsi_meet_domain);
|
||||
|
||||
if (not room) then
|
||||
return nil;
|
||||
|
@ -88,7 +181,7 @@ prosody.events.add_handler("pre-jitsi-authentication", function(session)
|
|||
-- 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);
|
||||
remove_poltergeist_occupant(room, nick, true);
|
||||
end
|
||||
|
||||
return username;
|
||||
|
@ -102,7 +195,8 @@ end);
|
|||
-- @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)
|
||||
-- @param status the initial status to use for the new occupant (optional)
|
||||
function create_poltergeist_occupant(room, nick, name, avatar, status)
|
||||
log("debug", "create_poltergeist_occupant %s:", nick);
|
||||
-- Join poltergeist occupant to room, with the invited JID as their nick
|
||||
local join_presence = st.presence({
|
||||
|
@ -118,22 +212,103 @@ function create_poltergeist_occupant(room, nick, name, avatar)
|
|||
if (avatar) then
|
||||
join_presence:tag("avatar-url"):text(avatar):up();
|
||||
end
|
||||
if (status) then
|
||||
join_presence:tag("status"):text(status):up();
|
||||
end
|
||||
|
||||
room:handle_first_presence(
|
||||
prosody.hosts[poltergeist_component], join_presence);
|
||||
|
||||
local timeout = poltergeist_timeout;
|
||||
-- the timeout before removing so participants can see the status update
|
||||
local removeTimeout = 5;
|
||||
if (poltergeistExpiredStatus) then
|
||||
timeout = timeout - removeTimeout;
|
||||
end
|
||||
|
||||
timer.add_task(timeout,
|
||||
function ()
|
||||
if (poltergeistExpiredStatus) then
|
||||
update_poltergeist_occupant_status(
|
||||
room, nick, poltergeistExpiredStatus);
|
||||
-- and remove it after some time so participant can see
|
||||
-- the update
|
||||
timer.add_task(removeTimeout,
|
||||
function ()
|
||||
if (have_poltergeist_occupant(room, nick)) then
|
||||
remove_poltergeist_occupant(room, nick, false);
|
||||
end
|
||||
end);
|
||||
else
|
||||
if (have_poltergeist_occupant(room, nick)) then
|
||||
remove_poltergeist_occupant(room, nick, false);
|
||||
end
|
||||
end
|
||||
end);
|
||||
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)
|
||||
-- @param ignore to mark the poltergeist unavailble presence to be ignored
|
||||
function remove_poltergeist_occupant(room, nick, ignore)
|
||||
log("debug", "remove_poltergeist_occupant %s", nick);
|
||||
local leave_presence = st.presence({
|
||||
to = room.jid.."/"..nick,
|
||||
from = poltergeist_component.."/"..nick,
|
||||
type = "unavailable" });
|
||||
if (ignore) then
|
||||
poltergeists_pr_ignore[room.jid.."/"..nick] = true;
|
||||
end
|
||||
room:handle_normal_presence(
|
||||
prosody.hosts[poltergeist_component], leave_presence);
|
||||
remove_username(room, nick);
|
||||
end
|
||||
|
||||
-- Updates poltergeist occupant status
|
||||
-- @param room the room instance where to remove the occupant
|
||||
-- @param nick the nick of the occupant to remove
|
||||
-- @param status the status to update
|
||||
function update_poltergeist_occupant_status(room, nick, status)
|
||||
local update_presence = get_presence(room, nick);
|
||||
|
||||
if (not update_presence) then
|
||||
-- no presence found for occupant, create one
|
||||
update_presence = st.presence({
|
||||
to = room.jid.."/"..nick,
|
||||
from = poltergeist_component.."/"..nick
|
||||
});
|
||||
else
|
||||
-- update occupant presence with appropriate to and from
|
||||
-- so we can send it again
|
||||
update_presence = st.clone(update_presence);
|
||||
update_presence.attr.to = room.jid.."/"..nick;
|
||||
update_presence.attr.from = poltergeist_component.."/"..nick;
|
||||
end
|
||||
|
||||
local once = false;
|
||||
-- the status tag we will attach
|
||||
local statusTag = st.stanza("status"):text(status);
|
||||
|
||||
-- if there is already a status tag replace it
|
||||
update_presence:maptags(function (tag)
|
||||
if tag.name == statusTag.name then
|
||||
if not once then
|
||||
once = true;
|
||||
return statusTag;
|
||||
else
|
||||
return nil;
|
||||
end
|
||||
end
|
||||
return tag;
|
||||
end);
|
||||
if (not once) then
|
||||
-- no status tag was repleced, attach it
|
||||
update_presence:add_child(statusTag);
|
||||
end
|
||||
|
||||
room:handle_normal_presence(
|
||||
prosody.hosts[poltergeist_component], update_presence);
|
||||
end
|
||||
|
||||
-- Checks for existance of a poltergeist occupant
|
||||
|
@ -145,12 +320,26 @@ function have_poltergeist_occupant(room, nick)
|
|||
return not not room:get_occupant_jid(poltergeist_component.."/"..nick);
|
||||
end
|
||||
|
||||
-- Returns the last presence of occupant
|
||||
-- @param room the room instance where to check for occupant
|
||||
-- @param nick the nick of the occupant
|
||||
-- @return presence of the occupant
|
||||
function get_presence(room, nick)
|
||||
local occupant_jid
|
||||
= room:get_occupant_jid(poltergeist_component.."/"..nick);
|
||||
if (occupant_jid) then
|
||||
return room:get_occupant_by_nick(occupant_jid):get_presence();
|
||||
end
|
||||
|
||||
return nil;
|
||||
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));
|
||||
remove_poltergeist_occupant(event.room, bare(event.stanza.attr.from), false);
|
||||
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
|
||||
|
@ -158,34 +347,54 @@ end, -100);
|
|||
-- 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
|
||||
if(event.stanza.attr.type == "unavailable"
|
||||
and poltergeists_pr_ignore[event.occupant.nick]) then
|
||||
event.stanza:tag(
|
||||
"ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
|
||||
poltergeists_pr_ignore[event.occupant.nick] = nil;
|
||||
end
|
||||
end
|
||||
end, -100);
|
||||
|
||||
-- cleanup room table after room is destroyed
|
||||
module:hook("muc-room-destroyed",function(event)
|
||||
local room_name = jid.node(event.room.jid);
|
||||
if (poltergeists[room_name]) then
|
||||
poltergeists[room_name] = nil;
|
||||
end
|
||||
end);
|
||||
|
||||
--- 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)
|
||||
if (not event.request.url.query) then
|
||||
return 400;
|
||||
end
|
||||
|
||||
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 status = params["status"];
|
||||
|
||||
if not verify_token(params["token"], room_name, group) then
|
||||
return 403;
|
||||
end
|
||||
|
||||
local room = get_room(room_name, group);
|
||||
if (not room) then
|
||||
log("error", "no room found %s", room_address);
|
||||
log("error", "no room found %s", room_name);
|
||||
return 404;
|
||||
end
|
||||
|
||||
local username = generate_uuid();
|
||||
store_username(room, user_id, username)
|
||||
|
||||
create_poltergeist_occupant(room, string.sub(username,0,8), name, avatar);
|
||||
create_poltergeist_occupant(
|
||||
room, string.sub(username,0,8), name, avatar, status);
|
||||
|
||||
return 200;
|
||||
end
|
||||
|
@ -194,15 +403,23 @@ end
|
|||
-- @param event the http event, holds the request query
|
||||
-- @return GET response, containing a json with response details
|
||||
function handle_update_poltergeist (event)
|
||||
if (not event.request.url.query) then
|
||||
return 400;
|
||||
end
|
||||
|
||||
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"];
|
||||
|
||||
if not verify_token(params["token"], room_name, group) then
|
||||
return 403;
|
||||
end
|
||||
|
||||
local room = get_room(room_name, group);
|
||||
if (not room) then
|
||||
log("error", "no room found %s", room_address);
|
||||
log("error", "no room found %s", room_name);
|
||||
return 404;
|
||||
end
|
||||
|
||||
|
@ -213,20 +430,49 @@ function handle_update_poltergeist (event)
|
|||
|
||||
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);
|
||||
|
||||
update_poltergeist_occupant_status(room, nick, status);
|
||||
return 200;
|
||||
else
|
||||
return 404;
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles remove poltergeists
|
||||
-- @param event the http event, holds the request query
|
||||
-- @return GET response, containing a json with response details
|
||||
function handle_remove_poltergeist (event)
|
||||
if (not event.request.url.query) then
|
||||
return 400;
|
||||
end
|
||||
|
||||
local params = parse(event.request.url.query);
|
||||
local user_id = params["user"];
|
||||
local room_name = params["room"];
|
||||
local group = params["group"];
|
||||
|
||||
if not verify_token(params["token"], room_name, group) then
|
||||
return 403;
|
||||
end
|
||||
|
||||
local room = get_room(room_name, group);
|
||||
if (not room) then
|
||||
log("error", "no room found %s", room_name);
|
||||
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
|
||||
remove_poltergeist_occupant(room, nick, false);
|
||||
return 200;
|
||||
else
|
||||
return 404;
|
||||
end
|
||||
end
|
||||
|
||||
log("info", "Loading poltergeist service");
|
||||
module:depends("http");
|
||||
|
@ -234,7 +480,8 @@ module:provides("http", {
|
|||
default_path = "/";
|
||||
name = "poltergeist";
|
||||
route = {
|
||||
["GET /poltergeist/create"] = handle_create_poltergeist;
|
||||
["GET /poltergeist/update"] = handle_update_poltergeist;
|
||||
["GET /poltergeist/create"] = function (event) return wrap_async_run(event,handle_create_poltergeist) end;
|
||||
["GET /poltergeist/update"] = function (event) return wrap_async_run(event,handle_update_poltergeist) end;
|
||||
["GET /poltergeist/remove"] = function (event) return wrap_async_run(event,handle_remove_poltergeist) end;
|
||||
};
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ local it = require "util.iterators";
|
|||
local json = require "util.json";
|
||||
local iterators = require "util.iterators";
|
||||
local array = require"util.array";
|
||||
local wrap_async_run = module:require "util".wrap_async_run;
|
||||
|
||||
local tostring = tostring;
|
||||
local neturl = require "net.url";
|
||||
|
@ -72,6 +73,10 @@ end
|
|||
-- @return GET response, containing a json with participants count,
|
||||
-- tha value is without counting the focus.
|
||||
function handle_get_room_size(event)
|
||||
if (not event.request.url.query) then
|
||||
return 400;
|
||||
end
|
||||
|
||||
local params = parse(event.request.url.query);
|
||||
local room_name = params["room"];
|
||||
local domain_name = params["domain"];
|
||||
|
@ -121,6 +126,10 @@ end
|
|||
-- @param event the http event, holds the request query
|
||||
-- @return GET response, containing a json with participants details
|
||||
function handle_get_room (event)
|
||||
if (not event.request.url.query) then
|
||||
return 400;
|
||||
end
|
||||
|
||||
local params = parse(event.request.url.query);
|
||||
local room_name = params["room"];
|
||||
local domain_name = params["domain"];
|
||||
|
@ -184,9 +193,9 @@ function module.load()
|
|||
module:provides("http", {
|
||||
default_path = "/";
|
||||
route = {
|
||||
["GET room-size"] = handle_get_room_size;
|
||||
["GET room-size"] = function (event) return wrap_async_run(event,handle_get_room_size) end;
|
||||
["GET sessions"] = function () return tostring(it.count(it.keys(prosody.full_sessions))); end;
|
||||
["GET room"] = handle_get_room;
|
||||
["GET room"] = function (event) return wrap_async_run(event,handle_get_room) end;
|
||||
};
|
||||
});
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local jid = require "util.jid";
|
||||
local runner, waiter = require "util.async".runner, require "util.async".waiter;
|
||||
|
||||
--- Finds and returns room by its jid
|
||||
-- @param room_jid the room jid to search in the muc component
|
||||
|
@ -20,6 +21,20 @@ function get_room_from_jid(room_jid)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function wrap_async_run(event,handler)
|
||||
local result;
|
||||
local async_func = runner(function (event)
|
||||
local wait, done = waiter();
|
||||
result=handler(event);
|
||||
done();
|
||||
return result;
|
||||
end)
|
||||
async_func:run(event)
|
||||
return result;
|
||||
end
|
||||
|
||||
return {
|
||||
get_room_from_jid = get_room_from_jid;
|
||||
wrap_async_run = wrap_async_run;
|
||||
};
|
Loading…
Reference in New Issue