"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-5186/resources/prosody-plugins/mod_muc_poltergeist.lua" (30 Jul 2021, 10759 Bytes) of package /linux/misc/jitsi-meet-5186.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Lua source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 local bare = require "util.jid".bare;
    2 local get_room_by_name_and_subdomain = module:require "util".get_room_by_name_and_subdomain;
    3 local jid = require "util.jid";
    4 local neturl = require "net.url";
    5 local parse = neturl.parseQuery;
    6 local poltergeist = module:require "poltergeist";
    7 
    8 local have_async = pcall(require, "util.async");
    9 if not have_async then
   10     module:log("error", "requires a version of Prosody with util.async");
   11     return;
   12 end
   13 
   14 module:depends("jitsi_session");
   15 
   16 local async_handler_wrapper = module:require "util".async_handler_wrapper;
   17 
   18 -- Options
   19 local poltergeist_component
   20     = module:get_option_string("poltergeist_component", module.host);
   21 
   22 -- this basically strips the domain from the conference.domain address
   23 local parentHostName = string.gmatch(tostring(module.host), "%w+.(%w.+)")();
   24 if parentHostName == nil then
   25     log("error", "Failed to start - unable to get parent hostname");
   26     return;
   27 end
   28 
   29 local parentCtx = module:context(parentHostName);
   30 if parentCtx == nil then
   31     log("error",
   32         "Failed to start - unable to get parent context for host: %s",
   33         tostring(parentHostName));
   34     return;
   35 end
   36 local token_util = module:require "token/util".new(parentCtx);
   37 
   38 -- option to enable/disable token verifications
   39 local disableTokenVerification
   40     = module:get_option_boolean("disable_polergeist_token_verification", false);
   41 
   42 -- poltergaist management functions
   43 
   44 --- Verifies room name, domain name with the values in the token
   45 -- @param token the token we received
   46 -- @param room_name the room name
   47 -- @param group name of the group (optional)
   48 -- @param session the session to use for storing token specific fields
   49 -- @return true if values are ok or false otherwise
   50 function verify_token(token, room_name, group, session)
   51     if disableTokenVerification then
   52         return true;
   53     end
   54 
   55     -- if not disableTokenVerification and we do not have token
   56     -- stop here, cause the main virtual host can have guest access enabled
   57     -- (allowEmptyToken = true) and we will allow access to rooms info without
   58     -- a token
   59     if token == nil then
   60         log("warn", "no token provided");
   61         return false;
   62     end
   63 
   64     session.auth_token = token;
   65     local verified, reason = token_util:process_and_verify_token(session);
   66     if not verified then
   67         log("warn", "not a valid token %s", tostring(reason));
   68         return false;
   69     end
   70 
   71     local room_address = jid.join(room_name, module:get_host());
   72     -- if there is a group we are in multidomain mode and that group is not
   73     -- our parent host
   74     if group and group ~= "" and group ~= parentHostName then
   75         room_address = "["..group.."]"..room_address;
   76     end
   77 
   78     if not token_util:verify_room(session, room_address) then
   79         log("warn", "Token %s not allowed to join: %s",
   80             tostring(token), tostring(room_address));
   81         return false;
   82     end
   83 
   84     return true;
   85 end
   86 
   87 -- Event handlers
   88 
   89 -- if we found that a session for a user with id has a poltergiest already
   90 -- created, retrieve its jid and return it to the authentication
   91 -- so we can reuse it and we that real user will replace the poltergiest
   92 prosody.events.add_handler("pre-jitsi-authentication", function(session)
   93 
   94     if (session.jitsi_meet_context_user) then
   95         local room = get_room_by_name_and_subdomain(
   96             session.jitsi_web_query_room,
   97             session.jitsi_web_query_prefix);
   98 
   99         if (not room) then
  100             return nil;
  101         end
  102 
  103         local username = poltergeist.get_username(
  104            room,
  105            session.jitsi_meet_context_user["id"]
  106         );
  107 
  108         if (not username) then
  109             return nil;
  110         end
  111 
  112         log("debug", "Found predefined username %s", username);
  113 
  114         -- let's find the room and if the poltergeist occupant is there
  115         -- lets remove him before the real participant joins
  116         -- when we see the unavailable presence to go out the server
  117         -- we will mark it with ignore tag
  118         local nick = poltergeist.create_nick(username);
  119         if (poltergeist.occupies(room, nick)) then
  120             module:log("info", "swapping poltergeist for user: %s/%s", room, nick)
  121             -- notify that user connected using the poltergeist
  122             poltergeist.update(room, nick, "connected");
  123             poltergeist.remove(room, nick, true);
  124         end
  125 
  126         return username;
  127     end
  128 
  129     return nil;
  130 end);
  131 
  132 --- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
  133 --- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
  134 module:hook("muc-decline", function (event)
  135     poltergeist.remove(event.room, bare(event.stanza.attr.from), false);
  136 end, -100);
  137 -- before sending the presence for a poltergeist leaving add ignore tag
  138 -- as poltergeist is leaving just before the real user joins and in the client
  139 -- we ignore this presence to avoid leaving/joining experience and the real
  140 -- user will reuse all currently created UI components for the same nick
  141 module:hook("muc-broadcast-presence", function (event)
  142     if (bare(event.occupant.jid) == poltergeist_component) then
  143         if(event.stanza.attr.type == "unavailable"
  144         and poltergeist.should_ignore(event.occupant.nick)) then
  145             event.stanza:tag(
  146                 "ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
  147                 poltergeist.reset_ignored(event.occupant.nick);
  148         end
  149     end
  150 end, -100);
  151 
  152 -- cleanup room table after room is destroyed
  153 module:hook(
  154    "muc-room-destroyed",
  155    function(event)
  156       poltergeist.remove_room(event.room);
  157    end
  158 );
  159 
  160 --- Handles request for creating/managing poltergeists
  161 -- @param event the http event, holds the request query
  162 -- @return GET response, containing a json with response details
  163 function handle_create_poltergeist (event)
  164     if (not event.request.url.query) then
  165         return { status_code = 400; };
  166     end
  167 
  168     local params = parse(event.request.url.query);
  169     local user_id = params["user"];
  170     local room_name = params["room"];
  171     local group = params["group"];
  172     local name = params["name"];
  173     local avatar = params["avatar"];
  174     local status = params["status"];
  175     local conversation = params["conversation"];
  176     local session = {};
  177 
  178     if not verify_token(params["token"], room_name, group, session) then
  179         return { status_code = 403; };
  180     end
  181 
  182     -- If the provided room conference doesn't exist then we
  183     -- can't add a poltergeist to it.
  184     local room = get_room_by_name_and_subdomain(room_name, group);
  185     if (not room) then
  186         log("error", "no room found %s", room_name);
  187         return { status_code = 404; };
  188     end
  189 
  190     -- If the poltergiest is already in the conference then it will
  191     -- be in our username store and another can't be added.
  192     local username = poltergeist.get_username(room, user_id);
  193     if (username ~=nil and
  194         poltergeist.occupies(room, poltergeist.create_nick(username))) then
  195         log("warn",
  196             "poltergeist for username:%s already in the room:%s",
  197             username,
  198             room_name
  199         );
  200         return { status_code = 202; };
  201     end
  202 
  203     local context = {
  204        user = {
  205            id = user_id;
  206        };
  207        group = group;
  208        creator_user = session.jitsi_meet_context_user;
  209        creator_group = session.jitsi_meet_context_group;
  210     };
  211     if avatar ~= nil then
  212         context.user.avatar = avatar
  213     end
  214     local resources = {};
  215     if conversation ~= nil then
  216         resources["conversation"] = conversation
  217     end
  218 
  219     poltergeist.add_to_muc(room, user_id, name, avatar, context, status, resources)
  220     return { status_code = 200; };
  221 end
  222 
  223 --- Handles request for updating poltergeists status
  224 -- @param event the http event, holds the request query
  225 -- @return GET response, containing a json with response details
  226 function handle_update_poltergeist (event)
  227     if (not event.request.url.query) then
  228         return { status_code = 400; };
  229     end
  230 
  231     local params = parse(event.request.url.query);
  232     local user_id = params["user"];
  233     local room_name = params["room"];
  234     local group = params["group"];
  235     local status = params["status"];
  236     local call_id = params["callid"];
  237 
  238     local call_cancel = false
  239     if params["callcancel"] == "true" then
  240        call_cancel = true;
  241     end
  242 
  243     if not verify_token(params["token"], room_name, group, {}) then
  244         return { status_code = 403; };
  245     end
  246 
  247     local room = get_room_by_name_and_subdomain(room_name, group);
  248     if (not room) then
  249         log("error", "no room found %s", room_name);
  250         return { status_code = 404; };
  251     end
  252 
  253     local username = poltergeist.get_username(room, user_id);
  254     if (not username) then
  255         return { status_code = 404; };
  256     end
  257 
  258     local call_details = {
  259         ["cancel"] = call_cancel;
  260         ["id"] = call_id;
  261     };
  262 
  263     local nick = poltergeist.create_nick(username);
  264     if (not poltergeist.occupies(room, nick)) then
  265        return { status_code = 404; };
  266     end
  267 
  268     poltergeist.update(room, nick, status, call_details);
  269     return { status_code = 200; };
  270 end
  271 
  272 --- Handles remove poltergeists
  273 -- @param event the http event, holds the request query
  274 -- @return GET response, containing a json with response details
  275 function handle_remove_poltergeist (event)
  276     if (not event.request.url.query) then
  277         return { status_code = 400; };
  278     end
  279 
  280     local params = parse(event.request.url.query);
  281     local user_id = params["user"];
  282     local room_name = params["room"];
  283     local group = params["group"];
  284 
  285     if not verify_token(params["token"], room_name, group, {}) then
  286         return { status_code = 403; };
  287     end
  288 
  289     local room = get_room_by_name_and_subdomain(room_name, group);
  290     if (not room) then
  291         log("error", "no room found %s", room_name);
  292         return { status_code = 404; };
  293     end
  294 
  295     local username = poltergeist.get_username(room, user_id);
  296     if (not username) then
  297         return { status_code = 404; };
  298     end
  299 
  300     local nick = poltergeist.create_nick(username);
  301     if (not poltergeist.occupies(room, nick)) then
  302        return { status_code = 404; };
  303     end
  304 
  305     poltergeist.remove(room, nick, false);
  306     return { status_code = 200; };
  307 end
  308 
  309 log("info", "Loading poltergeist service");
  310 module:depends("http");
  311 module:provides("http", {
  312     default_path = "/";
  313     name = "poltergeist";
  314     route = {
  315         ["GET /poltergeist/create"] = function (event) return async_handler_wrapper(event,handle_create_poltergeist) end;
  316         ["GET /poltergeist/update"] = function (event) return async_handler_wrapper(event,handle_update_poltergeist) end;
  317         ["GET /poltergeist/remove"] = function (event) return async_handler_wrapper(event,handle_remove_poltergeist) end;
  318     };
  319 });