123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- 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;
- 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);
-
- -- 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
-
- -- 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, true);
- 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);
-
- timer.add_task(poltergeist_timeout,
- function ()
- if (have_poltergeist_occupant(room, nick)) then
- remove_poltergeist_occupant(room, nick, false);
- 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
- -- @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);
- 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), 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
- -- 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"
- 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);
-
- --- 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 room = get_room(room_name, group);
- if (not room) then
- 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);
-
- 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)
- 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"];
-
- 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
- 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
-
- --- 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"];
-
- 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");
- module:provides("http", {
- default_path = "/";
- name = "poltergeist";
- route = {
- ["GET /poltergeist/create"] = handle_create_poltergeist;
- ["GET /poltergeist/update"] = handle_update_poltergeist;
- ["GET /poltergeist/remove"] = handle_remove_poltergeist;
- };
- });
|