"Fossies" - the Fresh Open Source Software Archive 
Member "jitsi-meet-6444/resources/prosody-plugins/mod_muc_poltergeist.lua" (8 Aug 2022, 10759 Bytes) of package /linux/misc/jitsi-meet-6444.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 });