"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-7553/resources/prosody-plugins/poltergeist.lib.lua" (27 Sep 2023, 13525 Bytes) of package /linux/misc/jitsi-meet-7553.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 inspect = require("inspect")
    2 local jid = require("util.jid")
    3 local stanza = require("util.stanza")
    4 local timer = require("util.timer")
    5 local update_presence_identity = module:require("util").update_presence_identity
    6 local uuid = require("util.uuid")
    7 
    8 local component = module:get_option_string(
    9     "poltergeist_component",
   10     module.host
   11 )
   12 
   13 local expiration_timeout = module:get_option_string(
   14     "poltergeist_leave_timeout",
   15     30 -- defaults to 30 seconds
   16 )
   17 
   18 local MUC_NS = "http://jabber.org/protocol/muc"
   19 
   20 --------------------------------------------------------------------------------
   21 -- Utility functions for commonly used poltergeist codes.
   22 --------------------------------------------------------------------------------
   23 
   24 -- Creates a nick for a poltergeist.
   25 -- @param username is the unique username of the poltergeist
   26 -- @return a nick to use for xmpp
   27 local function create_nick(username)
   28     return string.sub(username, 0,8)
   29 end
   30 
   31 -- Returns the last presence of the occupant.
   32 -- @param room the room instance where to check for occupant
   33 -- @param nick the nick of the occupant
   34 -- @return presence stanza of the occupant
   35 function get_presence(room, nick)
   36     local occupant_jid = room:get_occupant_jid(component.."/"..nick)
   37     if occupant_jid then
   38         return room:get_occupant_by_nick(occupant_jid):get_presence();
   39     end
   40     return nil;
   41 end
   42 
   43 -- Checks for existence of a poltergeist occupant in a room.
   44 -- @param room the room instance where to check for the occupant
   45 -- @param nick the nick of the occupant
   46 -- @return true if occupant is found, false otherwise
   47 function occupies(room, nick)
   48     -- Find out if we have a poltergeist occupant in the room for this JID
   49     return not not room:get_occupant_jid(component.."/"..nick);
   50 end
   51 
   52 --------------------------------------------------------------------------------
   53 -- Username storage for poltergeist.
   54 --
   55 -- Every poltergeist will have a username stored in a table underneath
   56 -- the room name that they are currently active in. The username can
   57 -- be retrieved given a room and a user_id. The username is removed from
   58 -- a room by providing the room and the nick.
   59 --
   60 -- A table with a single entry looks like:
   61 -- {
   62 --   ["[hug]hostilewerewolvesthinkslightly"] = {
   63 --     ["655363:52148a3e-b5fb-4cfc-8fbd-f55e793cf657"] = "ed7757d6-d88d-4e6a-8e24-aca2adc31348",
   64 --     ed7757d6 = "655363:52148a3e-b5fb-4cfc-8fbd-f55e793cf657"
   65 --   }
   66 -- }
   67 --------------------------------------------------------------------------------
   68 -- state is the table where poltergeist usernames and call resources are stored
   69 -- for a given xmpp muc.
   70 local state = module:shared("state")
   71 
   72 -- Adds a poltergeist to the store.
   73 -- @param room is the room the poltergeist is being added to
   74 -- @param user_id is the user_id of the user the poltergeist represents
   75 -- @param username is the unique id of the poltergeist itself
   76 local function store_username(room, user_id, username)
   77     local room_name = jid.node(room.jid)
   78 
   79     if not state[room_name] then
   80         state[room_name] = {}
   81     end
   82 
   83     state[room_name][user_id] = username
   84     state[room_name][create_nick(username)] = user_id
   85 end
   86 
   87 -- Retrieves a poltergeist username from the store if one exists.
   88 -- @param room is the room to check for the poltergeist in the store
   89 -- @param user_id is the user id of the user the poltergeist represents
   90 local function get_username(room, user_id)
   91     local room_name = jid.node(room.jid)
   92 
   93     if not state[room_name] then
   94         return nil
   95     end
   96 
   97     return state[room_name][user_id]
   98 end
   99 
  100 local function get_username_from_nick(room_name, nick)
  101     if not state[room_name] then
  102         return nil
  103     end
  104 
  105     local user_id = state[room_name][nick]
  106     return state[room_name][user_id]
  107 end
  108 
  109 -- Removes the username from the store.
  110 -- @param room is the room the poltergeist is being removed from
  111 -- @param nick is the nick of the muc occupant
  112 local function remove_username(room, nick)
  113     local room_name = jid.node(room.jid)
  114     if not state[room_name] then
  115         return
  116     end
  117 
  118     local user_id = state[room_name][nick]
  119     state[room_name][user_id] = nil
  120     state[room_name][nick] = nil
  121 end
  122 
  123 -- Removes all poltergeists in the store for the provided room.
  124 -- @param room is the room all poltergiest will be removed from
  125 local function remove_room(room)
  126     local room_name = jid.node(room.jid)
  127     if state[room_name] then
  128         state[room_name] = nil
  129     end
  130 end
  131 
  132 -- Adds a resource that is associated with a a call in a room. There
  133 -- is only one resource for each type.
  134 -- @param room is the room the call and poltergeist is in.
  135 -- @param call_id is the unique id for the call.
  136 -- @param resource_type is type of resource being added.
  137 -- @param resource_id is the id of the resource being added.
  138 local function add_call_resource(room, call_id, resource_type, resource_id)
  139     local room_name = jid.node(room.jid)
  140     if not state[room_name] then
  141         state[room_name] = {}
  142     end
  143 
  144     if not state[room_name][call_id] then
  145         state[room_name][call_id] = {}
  146     end
  147 
  148     state[room_name][call_id][resource_type] = resource_id
  149 end
  150 
  151 --------------------------------------------------------------------------------
  152 -- State for toggling the tagging of presence stanzas with ignored tag.
  153 --
  154 -- A poltergeist with it's full room/nick set to ignore will have a jitsi ignore
  155 -- tag applied to all presence stanza's broadcasted. The following functions
  156 -- assist in managing this state.
  157 --------------------------------------------------------------------------------
  158 local presence_ignored = {}
  159 
  160 -- Sets the nick to ignored state.
  161 -- @param room_nick full room/nick jid
  162 local function set_ignored(room_nick)
  163     presence_ignored[room_nick] = true
  164 end
  165 
  166 -- Resets the nick out of ignored state.
  167 -- @param room_nick full room/nick jid
  168 local function reset_ignored(room_nick)
  169     presence_ignored[room_nick] = nil
  170 end
  171 
  172 -- Determines whether or not the leave presence should be tagged with ignored.
  173 -- @param room_nick full room/nick jid
  174 local function should_ignore(room_nick)
  175     if presence_ignored[room_nick] == nil then
  176         return false
  177     end
  178     return presence_ignored[room_nick]
  179 end
  180 
  181 --------------------------------------------------------------------------------
  182 -- Poltergeist control functions for adding, updating and removing poltergeist.
  183 --------------------------------------------------------------------------------
  184 
  185 -- Updates the status tags and call flow tags of an existing poltergeist
  186 -- presence.
  187 -- @param presence_stanza is the actual presence stanza for a poltergeist.
  188 -- @param status is the new status to be updated in the stanza.
  189 -- @param call_details is a table of call flow signal information.
  190 function update_presence_tags(presence_stanza, status, call_details)
  191     local call_cancel = false
  192     local call_id = nil
  193 
  194     -- Extract optional call flow signal information.
  195     if call_details then
  196         call_id = call_details["id"]
  197 
  198         if call_details["cancel"] then
  199             call_cancel = call_details["cancel"]
  200         end
  201     end
  202 
  203     presence_stanza:maptags(function (tag)
  204         if tag.name == "status" then
  205             if call_cancel then
  206                 -- If call cancel is set then the status should not be changed.
  207                 return tag
  208             end
  209             return stanza.stanza("status"):text(status)
  210         elseif tag.name == "call_id" then
  211             if call_id then
  212                 return stanza.stanza("call_id"):text(call_id)
  213             else
  214                 -- If no call id is provided the re-use the existing id.
  215                 return tag
  216             end
  217         elseif tag.name == "call_cancel" then
  218             if call_cancel then
  219                 return stanza.stanza("call_cancel"):text("true")
  220             else
  221                 return stanza.stanza("call_cancel"):text("false")
  222             end
  223         end
  224         return tag
  225     end)
  226 
  227     return presence_stanza
  228 end
  229 
  230 -- Updates the presence status of a poltergeist.
  231 -- @param room is the room the poltergeist has occupied
  232 -- @param nick is the xmpp nick of the poltergeist occupant
  233 -- @param status is the status string to set in the presence
  234 -- @param call_details is a table of call flow control details
  235 local function update(room, nick, status, call_details)
  236     local original_presence = get_presence(room, nick)
  237 
  238     if not original_presence then
  239         module:log("info", "update issued for a non-existing poltergeist")
  240         return
  241     end
  242 
  243     -- update occupant presence with appropriate to and from
  244     -- so we can send it again
  245     update_presence = stanza.clone(original_presence)
  246     update_presence.attr.to = room.jid.."/"..nick
  247     update_presence.attr.from = component.."/"..nick
  248 
  249     update_presence = update_presence_tags(update_presence, status, call_details)
  250 
  251     module:log("info", "updating poltergeist: %s/%s - %s", room, nick, status)
  252     room:handle_normal_presence(
  253         prosody.hosts[component],
  254         update_presence
  255     )
  256 end
  257 
  258 -- Removes the poltergeist from the room.
  259 -- @param room is the room the poltergeist has occupied
  260 -- @param nick is the xmpp nick of the poltergeist occupant
  261 -- @param ignore toggles if the leave subsequent leave presence should be tagged
  262 local function remove(room, nick, ignore)
  263     local original_presence = get_presence(room, nick);
  264     if not original_presence then
  265         module:log("info", "attempted to remove a poltergeist with no presence")
  266         return
  267     end
  268 
  269     local leave_presence = stanza.clone(original_presence)
  270     leave_presence.attr.to = room.jid.."/"..nick
  271     leave_presence.attr.from = component.."/"..nick
  272     leave_presence.attr.type = "unavailable"
  273 
  274     if (ignore) then
  275         set_ignored(room.jid.."/"..nick)
  276     end
  277 
  278     remove_username(room, nick)
  279     module:log("info", "removing poltergeist: %s/%s", room, nick)
  280     room:handle_normal_presence(
  281         prosody.hosts[component],
  282         leave_presence
  283     )
  284 end
  285 
  286 -- Adds a poltergeist to a muc/room.
  287 -- @param room is the room the poltergeist will occupy
  288 -- @param is the id of the user the poltergeist represents
  289 -- @param display_name is the display name to use for the poltergeist
  290 -- @param avatar is the avatar link used for the poltergeist display
  291 -- @param context is the session context of the user making the request
  292 -- @param status is the presence status string to use
  293 -- @param resources is a table of resource types and resource ids to correlate.
  294 local function add_to_muc(room, user_id, display_name, avatar, context, status, resources)
  295     local username = uuid.generate()
  296     local presence_stanza = original_presence(
  297         room,
  298         username,
  299         display_name,
  300         avatar,
  301         context,
  302         status
  303     )
  304 
  305     module:log("info", "adding poltergeist: %s/%s", room, create_nick(username))
  306     store_username(room, user_id, username)
  307     for k, v in pairs(resources) do
  308         add_call_resource(room, username, k, v)
  309     end
  310     room:handle_first_presence(
  311         prosody.hosts[component],
  312         presence_stanza
  313     )
  314 
  315     local remove_delay = 5
  316     local expiration = expiration_timeout - remove_delay;
  317     local nick = create_nick(username)
  318     timer.add_task(
  319         expiration,
  320         function ()
  321             update(room, nick, "expired")
  322             timer.add_task(
  323                 remove_delay,
  324                 function ()
  325                     if occupies(room, nick) then
  326                         remove(room, nick, false)
  327                     end
  328                 end
  329             )
  330         end
  331     )
  332 end
  333 
  334 -- Generates an original presence for a new poltergeist
  335 -- @param room is the room the poltergeist will occupy
  336 -- @param username is the unique name for the poltergeist
  337 -- @param display_name is the display name to use for the poltergeist
  338 -- @param avatar is the avatar link used for the poltergeist display
  339 -- @param context is the session context of the user making the request
  340 -- @param status is the presence status string to use
  341 -- @return a presence stanza that can be used to add the poltergeist to the muc
  342 function original_presence(room, username, display_name, avatar, context, status)
  343     local nick = create_nick(username)
  344     local p = stanza.presence({
  345         to = room.jid.."/"..nick,
  346         from = component.."/"..nick,
  347     }):tag("x", { xmlns = MUC_NS }):up();
  348 
  349     p:tag("bot", { type = "poltergeist" }):up();
  350     p:tag("call_cancel"):text(nil):up();
  351     p:tag("call_id"):text(username):up();
  352 
  353     if status then
  354         p:tag("status"):text(status):up();
  355     else
  356         p:tag("status"):text(nil):up();
  357     end
  358 
  359     if display_name then
  360         p:tag(
  361             "nick",
  362             { xmlns = "http://jabber.org/protocol/nick" }):text(display_name):up();
  363     end
  364 
  365     if avatar then
  366         p:tag("avatar-url"):text(avatar):up();
  367     end
  368 
  369     -- If the room has a password set, let the poltergeist enter using it
  370     local room_password = room:get_password();
  371     if room_password then
  372         local join = p:get_child("x", MUC_NS);
  373         join:tag("password", { xmlns = MUC_NS }):text(room_password);
  374     end
  375 
  376     update_presence_identity(
  377         p,
  378         context.user,
  379         context.group,
  380         context.creator_user,
  381         context.creator_group
  382     )
  383     return p
  384 end
  385 
  386 return {
  387     get_username = get_username,
  388     get_username_from_nick = get_username_from_nick,
  389     occupies = occupies,
  390     remove_room = remove_room,
  391     reset_ignored = reset_ignored,
  392     should_ignore = should_ignore,
  393     create_nick = create_nick,
  394     add_to_muc = add_to_muc,
  395     update = update,
  396     remove = remove
  397 }