2017-07-15 03:18:23 +00:00
|
|
|
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)
|
2017-07-17 22:38:29 +00:00
|
|
|
if (not event.request.url.query) then
|
|
|
|
return 400;
|
|
|
|
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"];
|
|
|
|
|
|
|
|
local room = get_room(room_name, group);
|
|
|
|
if (not room) then
|
2017-07-17 22:26:47 +00:00
|
|
|
log("error", "no room found %s", room_name);
|
2017-07-15 03:18:23 +00:00
|
|
|
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)
|
2017-07-17 22:38:29 +00:00
|
|
|
if (not event.request.url.query) then
|
|
|
|
return 400;
|
|
|
|
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"];
|
|
|
|
|
|
|
|
local room = get_room(room_name, group);
|
|
|
|
if (not room) then
|
2017-07-17 22:26:47 +00:00
|
|
|
log("error", "no room found %s", room_name);
|
2017-07-15 03:18:23 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
});
|