"Fossies" - the Fresh Open Source Software Archive 
Member "jitsi-meet-6444/resources/prosody-plugins/util.lib.lua" (8 Aug 2022, 13702 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.
See also the last
Fossies "Diffs" side-by-side code changes report for "util.lib.lua":
jitsi-meet_7439_vs_jitsi-meet_7577.
1 local jid = require "util.jid";
2 local timer = require "util.timer";
3 local http = require "net.http";
4
5 local http_timeout = 30;
6 local have_async, async = pcall(require, "util.async");
7 local http_headers = {
8 ["User-Agent"] = "Prosody ("..prosody.version.."; "..prosody.platform..")"
9 };
10
11 local muc_domain_prefix = module:get_option_string("muc_mapper_domain_prefix", "conference");
12
13 -- defaults to module.host, the module that uses the utility
14 local muc_domain_base = module:get_option_string("muc_mapper_domain_base", module.host);
15
16 -- The "real" MUC domain that we are proxying to
17 local muc_domain = module:get_option_string("muc_mapper_domain", muc_domain_prefix.."."..muc_domain_base);
18
19 local escaped_muc_domain_base = muc_domain_base:gsub("%p", "%%%1");
20 local escaped_muc_domain_prefix = muc_domain_prefix:gsub("%p", "%%%1");
21 -- The pattern used to extract the target subdomain
22 -- (e.g. extract 'foo' from 'conference.foo.example.com')
23 local target_subdomain_pattern = "^"..escaped_muc_domain_prefix..".([^%.]+)%."..escaped_muc_domain_base;
24
25 -- table to store all incoming iqs without roomname in it, like discoinfo to the muc component
26 local roomless_iqs = {};
27
28 -- Utility function to split room JID to include room name and subdomain
29 -- (e.g. from room1@conference.foo.example.com/res returns (room1, example.com, res, foo))
30 local function room_jid_split_subdomain(room_jid)
31 local node, host, resource = jid.split(room_jid);
32
33 -- optimization, skip matching if there is no subdomain or it is not the muc component address at all
34 if host == muc_domain or not starts_with(host, muc_domain_prefix) then
35 return node, host, resource;
36 end
37
38 local target_subdomain = host and host:match(target_subdomain_pattern);
39 return node, host, resource, target_subdomain
40 end
41
42 --- Utility function to check and convert a room JID from
43 --- virtual room1@conference.foo.example.com to real [foo]room1@conference.example.com
44 -- @param room_jid the room jid to match and rewrite if needed
45 -- @param stanza the stanza
46 -- @return returns room jid [foo]room1@conference.example.com when it has subdomain
47 -- otherwise room1@conference.example.com(the room_jid value untouched)
48 local function room_jid_match_rewrite(room_jid, stanza)
49 local node, _, resource, target_subdomain = room_jid_split_subdomain(room_jid);
50 if not target_subdomain then
51 -- module:log("debug", "No need to rewrite out 'to' %s", room_jid);
52 return room_jid;
53 end
54 -- Ok, rewrite room_jid address to new format
55 local new_node, new_host, new_resource;
56 if node then
57 new_node, new_host, new_resource = "["..target_subdomain.."]"..node, muc_domain, resource;
58 else
59 -- module:log("debug", "No room name provided so rewriting only host 'to' %s", room_jid);
60 new_host, new_resource = muc_domain, resource;
61
62 if (stanza and stanza.attr and stanza.attr.id) then
63 roomless_iqs[stanza.attr.id] = stanza.attr.to;
64 end
65 end
66
67 return jid.join(new_node, new_host, new_resource);
68 end
69
70 -- Utility function to check and convert a room JID from real [foo]room1@muc.example.com to virtual room1@muc.foo.example.com
71 local function internal_room_jid_match_rewrite(room_jid, stanza)
72 local node, host, resource = jid.split(room_jid);
73 if host ~= muc_domain or not node then
74 -- module:log("debug", "No need to rewrite %s (not from the MUC host)", room_jid);
75
76 if (stanza and stanza.attr and stanza.attr.id and roomless_iqs[stanza.attr.id]) then
77 local result = roomless_iqs[stanza.attr.id];
78 roomless_iqs[stanza.attr.id] = nil;
79 return result;
80 end
81
82 return room_jid;
83 end
84
85 local target_subdomain, target_node = extract_subdomain(node);
86 if not (target_node and target_subdomain) then
87 -- module:log("debug", "Not rewriting... unexpected node format: %s", node);
88 return room_jid;
89 end
90
91 -- Ok, rewrite room_jid address to pretty format
92 return jid.join(target_node, muc_domain_prefix..".".. target_subdomain.."."..muc_domain_base, resource);
93 end
94
95 --- Finds and returns room by its jid
96 -- @param room_jid the room jid to search in the muc component
97 -- @return returns room if found or nil
98 function get_room_from_jid(room_jid)
99 local _, host = jid.split(room_jid);
100 local component = hosts[host];
101 if component then
102 local muc = component.modules.muc
103 if muc and rawget(muc,"rooms") then
104 -- We're running 0.9.x or 0.10 (old MUC API)
105 return muc.rooms[room_jid];
106 elseif muc and rawget(muc,"get_room_from_jid") then
107 -- We're running >0.10 (new MUC API)
108 return muc.get_room_from_jid(room_jid);
109 else
110 return
111 end
112 end
113 end
114
115 -- Returns the room if available, work and in multidomain mode
116 -- @param room_name the name of the room
117 -- @param group name of the group (optional)
118 -- @return returns room if found or nil
119 function get_room_by_name_and_subdomain(room_name, subdomain)
120 local room_address;
121
122 -- if there is a subdomain we are in multidomain mode and that subdomain is not our main host
123 if subdomain and subdomain ~= "" and subdomain ~= muc_domain_base then
124 room_address = jid.join("["..subdomain.."]"..room_name, muc_domain);
125 else
126 room_address = jid.join(room_name, muc_domain);
127 end
128
129 return get_room_from_jid(room_address);
130 end
131
132 function async_handler_wrapper(event, handler)
133 if not have_async then
134 module:log("error", "requires a version of Prosody with util.async");
135 return nil;
136 end
137
138 local runner = async.runner;
139
140 -- Grab a local response so that we can send the http response when
141 -- the handler is done.
142 local response = event.response;
143 local async_func = runner(
144 function (event)
145 local result = handler(event)
146
147 -- If there is a status code in the result from the
148 -- wrapped handler then add it to the response.
149 if tonumber(result.status_code) ~= nil then
150 response.status_code = result.status_code
151 end
152
153 -- If there are headers in the result from the
154 -- wrapped handler then add them to the response.
155 if result.headers ~= nil then
156 response.headers = result.headers
157 end
158
159 -- Send the response to the waiting http client with
160 -- or without the body from the wrapped handler.
161 if result.body ~= nil then
162 response:send(result.body)
163 else
164 response:send();
165 end
166 end
167 )
168 async_func:run(event)
169 -- return true to keep the client http connection open.
170 return true;
171 end
172
173 --- Updates presence stanza, by adding identity node
174 -- @param stanza the presence stanza
175 -- @param user the user to which presence we are updating identity
176 -- @param group the group of the user to which presence we are updating identity
177 -- @param creator_user the user who created the user which presence we
178 -- are updating (this is the poltergeist case, where a user creates
179 -- a poltergeist), optional.
180 -- @param creator_group the group of the user who created the user which
181 -- presence we are updating (this is the poltergeist case, where a user creates
182 -- a poltergeist), optional.
183 function update_presence_identity(
184 stanza, user, group, creator_user, creator_group)
185
186 -- First remove any 'identity' element if it already
187 -- exists, so it cannot be spoofed by a client
188 stanza:maptags(
189 function(tag)
190 for k, v in pairs(tag) do
191 if k == "name" and v == "identity" then
192 return nil
193 end
194 end
195 return tag
196 end
197 )
198
199 stanza:tag("identity"):tag("user");
200 for k, v in pairs(user) do
201 v = tostring(v)
202 stanza:tag(k):text(v):up();
203 end
204 stanza:up();
205
206 -- Add the group information if it is present
207 if group then
208 stanza:tag("group"):text(group):up();
209 end
210
211 -- Add the creator user information if it is present
212 if creator_user then
213 stanza:tag("creator_user");
214 for k, v in pairs(creator_user) do
215 stanza:tag(k):text(v):up();
216 end
217 stanza:up();
218
219 -- Add the creator group information if it is present
220 if creator_group then
221 stanza:tag("creator_group"):text(creator_group):up();
222 end
223 stanza:up();
224 end
225
226 end
227
228 -- Utility function to check whether feature is present and enabled. Allow
229 -- a feature if there are features present in the session(coming from
230 -- the token) and the value of the feature is true.
231 -- If features is not present in the token we skip feature detection and allow
232 -- everything.
233 function is_feature_allowed(session, feature)
234 if (session.jitsi_meet_context_features == nil
235 or session.jitsi_meet_context_features[feature] == "true" or session.jitsi_meet_context_features[feature] == true) then
236 return true;
237 else
238 return false;
239 end
240 end
241
242 --- Extracts the subdomain and room name from internal jid node [foo]room1
243 -- @return subdomain(optional, if extracted or nil), the room name
244 function extract_subdomain(room_node)
245 -- optimization, skip matching if there is no subdomain, no [subdomain] part in the beginning of the node
246 if not starts_with(room_node, '[') then
247 return nil,room_node;
248 end
249
250 return room_node:match("^%[([^%]]+)%](.+)$");
251 end
252
253 function starts_with(str, start)
254 return str:sub(1, #start) == start
255 end
256
257 function ends_with(str, ending)
258 return ending == "" or str:sub(-#ending) == ending
259 end
260
261 -- healthcheck rooms in jicofo starts with a string '__jicofo-health-check'
262 function is_healthcheck_room(room_jid)
263 if starts_with(room_jid, "__jicofo-health-check") then
264 return true;
265 end
266
267 return false;
268 end
269
270 --- Utility function to make an http get request and
271 --- retry @param retry number of times
272 -- @param url endpoint to be called
273 -- @param retry nr of retries, if retry is
274 -- @param auth_token value to be passed as auth Bearer
275 -- nil there will be no retries
276 -- @returns result of the http call or nil if
277 -- the external call failed after the last retry
278 function http_get_with_retry(url, retry, auth_token)
279 local content, code;
280 local timeout_occurred;
281 local wait, done = async.waiter();
282 local request_headers = http_headers or {}
283 if auth_token ~= nil then
284 request_headers['Authorization'] = 'Bearer ' .. auth_token
285 end
286
287 local function cb(content_, code_, response_, request_)
288 if timeout_occurred == nil then
289 code = code_;
290 if code == 200 or code == 204 then
291 module:log("debug", "External call was successful, content %s", content_);
292 content = content_
293 else
294 module:log("warn", "Error on GET request: Code %s, Content %s",
295 code_, content_);
296 end
297 done();
298 else
299 module:log("warn", "External call reply delivered after timeout from: %s", url);
300 end
301 end
302
303 local function call_http()
304 return http.request(url, {
305 headers = request_headers,
306 method = "GET"
307 }, cb);
308 end
309
310 local request = call_http();
311
312 local function cancel()
313 -- TODO: This check is racey. Not likely to be a problem, but we should
314 -- still stick a mutex on content / code at some point.
315 if code == nil then
316 timeout_occurred = true;
317 module:log("warn", "Timeout %s seconds making the external call to: %s", http_timeout, url);
318 -- no longer present in prosody 0.11, so check before calling
319 if http.destroy_request ~= nil then
320 http.destroy_request(request);
321 end
322 if retry == nil then
323 module:log("debug", "External call failed and retry policy is not set");
324 done();
325 elseif retry ~= nil and retry < 1 then
326 module:log("debug", "External call failed after retry")
327 done();
328 else
329 module:log("debug", "External call failed, retry nr %s", retry)
330 retry = retry - 1;
331 request = call_http()
332 return http_timeout;
333 end
334 end
335 end
336 timer.add_task(http_timeout, cancel);
337 wait();
338
339 return content, code;
340 end
341
342 -- Checks whether there is status in the <x node
343 -- @param muc_x the <x element from presence
344 -- @param status checks for this status
345 -- @returns true if the status is found, false otherwise or if no muc_x is provided.
346 function presence_check_status(muc_x, status)
347 if not muc_x then
348 return false;
349 end
350
351 for statusNode in muc_x:childtags('status') do
352 if statusNode.attr.code == status then
353 return true;
354 end
355 end
356
357 return false;
358 end
359
360 return {
361 extract_subdomain = extract_subdomain;
362 is_feature_allowed = is_feature_allowed;
363 is_healthcheck_room = is_healthcheck_room;
364 get_room_from_jid = get_room_from_jid;
365 get_room_by_name_and_subdomain = get_room_by_name_and_subdomain;
366 async_handler_wrapper = async_handler_wrapper;
367 presence_check_status = presence_check_status;
368 room_jid_match_rewrite = room_jid_match_rewrite;
369 room_jid_split_subdomain = room_jid_split_subdomain;
370 internal_room_jid_match_rewrite = internal_room_jid_match_rewrite;
371 update_presence_identity = update_presence_identity;
372 http_get_with_retry = http_get_with_retry;
373 ends_with = ends_with;
374 starts_with = starts_with;
375 };