2017-07-15 03:18:23 +00:00
|
|
|
local bare = require "util.jid".bare;
|
2021-05-12 21:36:02 +00:00
|
|
|
local get_room_by_name_and_subdomain = module:require "util".get_room_by_name_and_subdomain;
|
2017-07-15 03:18:23 +00:00
|
|
|
local jid = require "util.jid";
|
|
|
|
local neturl = require "net.url";
|
|
|
|
local parse = neturl.parseQuery;
|
2018-06-15 19:28:05 +00:00
|
|
|
local poltergeist = module:require "poltergeist";
|
2019-12-05 10:09:58 +00:00
|
|
|
|
|
|
|
local have_async = pcall(require, "util.async");
|
|
|
|
if not have_async then
|
|
|
|
module:log("error", "requires a version of Prosody with util.async");
|
|
|
|
return;
|
|
|
|
end
|
|
|
|
|
2021-05-13 03:02:51 +00:00
|
|
|
module:depends("jitsi_session");
|
|
|
|
|
2020-01-23 19:31:05 +00:00
|
|
|
local async_handler_wrapper = module:require "util".async_handler_wrapper;
|
2017-07-15 03:18:23 +00:00
|
|
|
|
|
|
|
-- Options
|
|
|
|
local poltergeist_component
|
|
|
|
= module:get_option_string("poltergeist_component", module.host);
|
2018-06-15 19:28:05 +00:00
|
|
|
|
2017-07-19 16:36:49 +00:00
|
|
|
-- 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
|
2017-07-15 03:18:23 +00:00
|
|
|
|
2017-07-20 18:56:55 +00:00
|
|
|
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);
|
|
|
|
|
2017-07-15 03:18:23 +00:00
|
|
|
-- poltergaist management functions
|
|
|
|
|
2017-07-20 18:56:55 +00:00
|
|
|
--- 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)
|
2018-03-23 20:58:05 +00:00
|
|
|
-- @param session the session to use for storing token specific fields
|
2017-07-20 18:56:55 +00:00
|
|
|
-- @return true if values are ok or false otherwise
|
2018-03-23 20:58:05 +00:00
|
|
|
function verify_token(token, room_name, group, session)
|
2017-07-20 18:56:55 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
-- Event handlers
|
|
|
|
|
2017-07-15 03:18:23 +00:00
|
|
|
-- 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
|
2021-05-12 21:36:02 +00:00
|
|
|
local room = get_room_by_name_and_subdomain(
|
2020-06-30 13:15:08 +00:00
|
|
|
session.jitsi_web_query_room,
|
|
|
|
session.jitsi_web_query_prefix);
|
2017-07-15 03:18:23 +00:00
|
|
|
|
|
|
|
if (not room) then
|
|
|
|
return nil;
|
|
|
|
end
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local username = poltergeist.get_username(
|
|
|
|
room,
|
|
|
|
session.jitsi_meet_context_user["id"]
|
|
|
|
);
|
2017-07-15 03:18:23 +00:00
|
|
|
|
|
|
|
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
|
2018-06-15 19:28:05 +00:00
|
|
|
local nick = poltergeist.create_nick(username);
|
|
|
|
if (poltergeist.occupies(room, nick)) then
|
|
|
|
module:log("info", "swapping poltergeist for user: %s/%s", room, nick)
|
2017-08-23 18:41:55 +00:00
|
|
|
-- notify that user connected using the poltergeist
|
2018-06-15 19:28:05 +00:00
|
|
|
poltergeist.update(room, nick, "connected");
|
|
|
|
poltergeist.remove(room, nick, true);
|
2017-07-15 03:18:23 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return username;
|
|
|
|
end
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
end);
|
|
|
|
|
|
|
|
--- 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)
|
2018-06-15 19:28:05 +00:00
|
|
|
poltergeist.remove(event.room, bare(event.stanza.attr.from), false);
|
2017-07-15 03:18:23 +00:00
|
|
|
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
|
2017-07-18 00:29:00 +00:00
|
|
|
if(event.stanza.attr.type == "unavailable"
|
2018-06-15 19:28:05 +00:00
|
|
|
and poltergeist.should_ignore(event.occupant.nick)) then
|
2017-07-15 03:18:23 +00:00
|
|
|
event.stanza:tag(
|
|
|
|
"ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
|
2018-06-15 19:28:05 +00:00
|
|
|
poltergeist.reset_ignored(event.occupant.nick);
|
2017-07-15 03:18:23 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end, -100);
|
|
|
|
|
2017-07-18 16:11:14 +00:00
|
|
|
-- cleanup room table after room is destroyed
|
2018-06-15 19:28:05 +00:00
|
|
|
module:hook(
|
|
|
|
"muc-room-destroyed",
|
|
|
|
function(event)
|
|
|
|
poltergeist.remove_room(event.room);
|
|
|
|
end
|
|
|
|
);
|
2017-07-18 16:11:14 +00:00
|
|
|
|
2017-07-15 03:18:23 +00:00
|
|
|
--- 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)
|
2017-07-17 22:38:29 +00:00
|
|
|
if (not event.request.url.query) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 400; };
|
2017-07-17 22:38:29 +00:00
|
|
|
end
|
|
|
|
|
2017-07-15 03:18:23 +00:00
|
|
|
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"];
|
2017-07-20 18:29:54 +00:00
|
|
|
local status = params["status"];
|
2018-06-29 18:09:37 +00:00
|
|
|
local conversation = params["conversation"];
|
2018-03-23 20:58:05 +00:00
|
|
|
local session = {};
|
2017-07-15 03:18:23 +00:00
|
|
|
|
2018-03-23 20:58:05 +00:00
|
|
|
if not verify_token(params["token"], room_name, group, session) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 403; };
|
2017-07-20 18:56:55 +00:00
|
|
|
end
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
-- If the provided room conference doesn't exist then we
|
|
|
|
-- can't add a poltergeist to it.
|
2021-05-12 21:36:02 +00:00
|
|
|
local room = get_room_by_name_and_subdomain(room_name, group);
|
2017-07-15 03:18:23 +00:00
|
|
|
if (not room) then
|
2017-07-17 22:26:47 +00:00
|
|
|
log("error", "no room found %s", room_name);
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 404; };
|
2017-07-15 03:18:23 +00:00
|
|
|
end
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
-- If the poltergiest is already in the conference then it will
|
|
|
|
-- be in our username store and another can't be added.
|
2018-06-15 19:28:05 +00:00
|
|
|
local username = poltergeist.get_username(room, user_id);
|
|
|
|
if (username ~=nil and
|
|
|
|
poltergeist.occupies(room, poltergeist.create_nick(username))) then
|
|
|
|
log("warn",
|
|
|
|
"poltergeist for username:%s already in the room:%s",
|
|
|
|
username,
|
|
|
|
room_name
|
|
|
|
);
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 202; };
|
2017-08-23 21:14:44 +00:00
|
|
|
end
|
2018-06-15 19:28:05 +00:00
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local context = {
|
|
|
|
user = {
|
2018-06-15 19:28:05 +00:00
|
|
|
id = user_id;
|
2018-06-15 19:28:05 +00:00
|
|
|
};
|
|
|
|
group = group;
|
|
|
|
creator_user = session.jitsi_meet_context_user;
|
|
|
|
creator_group = session.jitsi_meet_context_group;
|
|
|
|
};
|
2020-07-08 08:09:39 +00:00
|
|
|
if avatar ~= nil then
|
|
|
|
context.user.avatar = avatar
|
|
|
|
end
|
2018-06-29 18:09:37 +00:00
|
|
|
local resources = {};
|
|
|
|
if conversation ~= nil then
|
|
|
|
resources["conversation"] = conversation
|
|
|
|
end
|
|
|
|
|
|
|
|
poltergeist.add_to_muc(room, user_id, name, avatar, context, status, resources)
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 200; };
|
2018-06-15 19:28:05 +00:00
|
|
|
end
|
|
|
|
|
2017-07-15 03:18:23 +00:00
|
|
|
--- 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)
|
2017-07-17 22:38:29 +00:00
|
|
|
if (not event.request.url.query) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 400; };
|
2017-07-17 22:38:29 +00:00
|
|
|
end
|
|
|
|
|
2017-07-15 03:18:23 +00:00
|
|
|
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"];
|
2018-06-15 19:28:05 +00:00
|
|
|
local call_id = params["callid"];
|
2018-05-24 15:20:52 +00:00
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local call_cancel = false
|
|
|
|
if params["callcancel"] == "true" then
|
|
|
|
call_cancel = true;
|
|
|
|
end
|
2017-07-15 03:18:23 +00:00
|
|
|
|
2018-03-23 20:58:05 +00:00
|
|
|
if not verify_token(params["token"], room_name, group, {}) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 403; };
|
2017-07-20 18:56:55 +00:00
|
|
|
end
|
|
|
|
|
2021-05-12 21:36:02 +00:00
|
|
|
local room = get_room_by_name_and_subdomain(room_name, group);
|
2017-07-15 03:18:23 +00:00
|
|
|
if (not room) then
|
2017-07-17 22:26:47 +00:00
|
|
|
log("error", "no room found %s", room_name);
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 404; };
|
2017-07-15 03:18:23 +00:00
|
|
|
end
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local username = poltergeist.get_username(room, user_id);
|
2017-07-15 03:18:23 +00:00
|
|
|
if (not username) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 404; };
|
2017-07-15 03:18:23 +00:00
|
|
|
end
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local call_details = {
|
|
|
|
["cancel"] = call_cancel;
|
|
|
|
["id"] = call_id;
|
|
|
|
};
|
2018-05-24 15:20:52 +00:00
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local nick = poltergeist.create_nick(username);
|
|
|
|
if (not poltergeist.occupies(room, nick)) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 404; };
|
2017-07-15 03:18:23 +00:00
|
|
|
end
|
2018-06-15 19:28:05 +00:00
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
poltergeist.update(room, nick, status, call_details);
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 200; };
|
2017-07-15 03:18:23 +00:00
|
|
|
end
|
|
|
|
|
2017-07-18 00:29:00 +00:00
|
|
|
--- 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
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 400; };
|
2017-07-18 00:29:00 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local params = parse(event.request.url.query);
|
|
|
|
local user_id = params["user"];
|
|
|
|
local room_name = params["room"];
|
|
|
|
local group = params["group"];
|
|
|
|
|
2018-03-23 20:58:05 +00:00
|
|
|
if not verify_token(params["token"], room_name, group, {}) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 403; };
|
2017-07-20 18:56:55 +00:00
|
|
|
end
|
|
|
|
|
2021-05-12 21:36:02 +00:00
|
|
|
local room = get_room_by_name_and_subdomain(room_name, group);
|
2017-07-18 00:29:00 +00:00
|
|
|
if (not room) then
|
|
|
|
log("error", "no room found %s", room_name);
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 404; };
|
2017-07-18 00:29:00 +00:00
|
|
|
end
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local username = poltergeist.get_username(room, user_id);
|
2017-07-18 00:29:00 +00:00
|
|
|
if (not username) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 404; };
|
2017-07-18 00:29:00 +00:00
|
|
|
end
|
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
local nick = poltergeist.create_nick(username);
|
|
|
|
if (not poltergeist.occupies(room, nick)) then
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 404; };
|
2017-07-18 00:29:00 +00:00
|
|
|
end
|
2018-06-15 19:28:05 +00:00
|
|
|
|
2018-06-15 19:28:05 +00:00
|
|
|
poltergeist.remove(room, nick, false);
|
2020-01-23 19:31:05 +00:00
|
|
|
return { status_code = 200; };
|
2017-07-18 00:29:00 +00:00
|
|
|
end
|
|
|
|
|
2017-07-15 03:18:23 +00:00
|
|
|
log("info", "Loading poltergeist service");
|
|
|
|
module:depends("http");
|
|
|
|
module:provides("http", {
|
|
|
|
default_path = "/";
|
|
|
|
name = "poltergeist";
|
|
|
|
route = {
|
2020-01-23 19:31:05 +00:00
|
|
|
["GET /poltergeist/create"] = function (event) return async_handler_wrapper(event,handle_create_poltergeist) end;
|
|
|
|
["GET /poltergeist/update"] = function (event) return async_handler_wrapper(event,handle_update_poltergeist) end;
|
|
|
|
["GET /poltergeist/remove"] = function (event) return async_handler_wrapper(event,handle_remove_poltergeist) end;
|
2017-07-15 03:18:23 +00:00
|
|
|
};
|
|
|
|
});
|