Procházet zdrojové kódy

Creating a poltergiest library and using in for mod_muc_poltergeist.

master
Jacob MacElroy před 7 roky
rodič
revize
6ae5adcb3d

+ 1
- 2
resources/prosody-plugins/mod_muc_call.lua Zobrazit soubor

39
     if not(target_node or target_subdomain) then
39
     if not(target_node or target_subdomain) then
40
         return "https://"..muc_domain_base.."/"..node
40
         return "https://"..muc_domain_base.."/"..node
41
     else
41
     else
42
-        return
43
-        "https://"..muc_domain_base.."/"..target_subdomain.."/"..target_node
42
+        return "https://"..muc_domain_base.."/"..target_subdomain.."/"..target_node
44
     end
43
     end
45
 end
44
 end
46
 
45
 

+ 46
- 265
resources/prosody-plugins/mod_muc_poltergeist.lua Zobrazit soubor

1
 local bare = require "util.jid".bare;
1
 local bare = require "util.jid".bare;
2
-local generate_uuid = require "util.uuid".generate;
2
+local get_room_from_jid = module:require "util".get_room_from_jid;
3
 local jid = require "util.jid";
3
 local jid = require "util.jid";
4
 local neturl = require "net.url";
4
 local neturl = require "net.url";
5
 local parse = neturl.parseQuery;
5
 local parse = neturl.parseQuery;
6
-local st = require "util.stanza";
7
-local get_room_from_jid = module:require "util".get_room_from_jid;
6
+local poltergeist = module:require "poltergeist";
8
 local wrap_async_run = module:require "util".wrap_async_run;
7
 local wrap_async_run = module:require "util".wrap_async_run;
9
-local update_presence_identity = module:require "util".update_presence_identity;
10
-local timer = require "util.timer";
11
-local MUC_NS = "http://jabber.org/protocol/muc";
12
-local expired_status  = "expired";
13
 
8
 
14
 -- Options
9
 -- Options
15
 local poltergeist_component
10
 local poltergeist_component
16
     = module:get_option_string("poltergeist_component", module.host);
11
     = module:get_option_string("poltergeist_component", module.host);
17
-local poltergeist_timeout
18
-    = module:get_option_string("poltergeist_leave_timeout", 30);
12
+
19
 -- this basically strips the domain from the conference.domain address
13
 -- this basically strips the domain from the conference.domain address
20
 local parentHostName = string.gmatch(tostring(module.host), "%w+.(%w.+)")();
14
 local parentHostName = string.gmatch(tostring(module.host), "%w+.(%w.+)")();
21
 if parentHostName == nil then
15
 if parentHostName == nil then
37
 local disableTokenVerification
30
 local disableTokenVerification
38
     = module:get_option_boolean("disable_polergeist_token_verification", false);
31
     = module:get_option_boolean("disable_polergeist_token_verification", false);
39
 
32
 
40
-local poltergeists = {};
41
-local poltergeists_pr_ignore = {};
42
-
43
 -- poltergaist management functions
33
 -- poltergaist management functions
44
 
34
 
45
 -- Returns the room if available, work and in multidomain mode
35
 -- Returns the room if available, work and in multidomain mode
60
     return get_room_from_jid(room_address);
47
     return get_room_from_jid(room_address);
61
 end
48
 end
62
 
49
 
63
-function store_username(room, user_id, username)
64
-    local room_name = jid.node(room.jid);
65
-
66
-    -- we store in poltergeist user ids for room names
67
-    if (not poltergeists[room_name]) then
68
-        poltergeists[room_name] = {};
69
-    end
70
-    poltergeists[room_name][user_id] = username;
71
-    log("debug", "stored in session: %s", username);
72
-end
73
-
74
-function get_username(room, user_id)
75
-    local room_name = jid.node(room.jid);
76
-
77
-    if (not poltergeists[room_name]) then
78
-        return nil;
79
-    end
80
-
81
-    return poltergeists[room_name][user_id];
82
-end
83
-
84
-function remove_username(room, nick)
85
-    local room_name = jid.node(room.jid);
86
-    if (poltergeists[room_name]) then
87
-        local user_id_to_remove;
88
-        for name,username in pairs(poltergeists[room_name]) do
89
-            if (string.sub(username, 0, 8) == nick) then
90
-                user_id_to_remove = name;
91
-            end
92
-        end
93
-        if (user_id_to_remove) then
94
-            poltergeists[room_name][user_id_to_remove] = nil;
95
-        end
96
-    end
97
-end
98
-
99
 --- Verifies room name, domain name with the values in the token
50
 --- Verifies room name, domain name with the values in the token
100
 -- @param token the token we received
51
 -- @param token the token we received
101
 -- @param room_name the room name
52
 -- @param room_name the room name
151
     return true;
90
     return true;
152
 end
91
 end
153
 
92
 
93
+-- Event handlers
94
+
154
 -- if we found that a session for a user with id has a poltergiest already
95
 -- if we found that a session for a user with id has a poltergiest already
155
 -- created, retrieve its jid and return it to the authentication
96
 -- created, retrieve its jid and return it to the authentication
156
 -- so we can reuse it and we that real user will replace the poltergiest
97
 -- so we can reuse it and we that real user will replace the poltergiest
165
             return nil;
106
             return nil;
166
         end
107
         end
167
 
108
 
168
-        local username
169
-            = get_username(room, session.jitsi_meet_context_user["id"]);
109
+        local username = poltergeist.get_username(
110
+           room,
111
+           session.jitsi_meet_context_user["id"]
112
+        );
170
 
113
 
171
         if (not username) then
114
         if (not username) then
172
             return nil;
115
             return nil;
178
         -- lets remove him before the real participant joins
121
         -- lets remove him before the real participant joins
179
         -- when we see the unavailable presence to go out the server
122
         -- when we see the unavailable presence to go out the server
180
         -- we will mark it with ignore tag
123
         -- we will mark it with ignore tag
181
-        local nick = string.sub(username, 0, 8);
182
-        if (have_poltergeist_occupant(room, nick)) then
124
+        local nick = poltergeist.create_nick(username);
125
+        if (poltergeist.occupies(room, nick)) then
126
+            module:log("info", "swapping poltergeist for user: %s/%s", room, nick)
183
             -- notify that user connected using the poltergeist
127
             -- notify that user connected using the poltergeist
184
-            update_poltergeist_occupant_status(
185
-			   room, nick, "connected");
186
-            remove_poltergeist_occupant(room, nick, true);
128
+            poltergeist.update(room, nick, "connected");
129
+            poltergeist.remove(room, nick, true);
187
         end
130
         end
188
 
131
 
189
         return username;
132
         return username;
192
     return nil;
135
     return nil;
193
 end);
136
 end);
194
 
137
 
195
-function remove_poltergeist_occupant(room, nick, ignore)
196
-   log("debug", "remove_poltergeist_occupant %s", nick);
197
-
198
-   local current_presence = get_presence(room, nick);
199
-   if (not current_presence) then
200
-      module:log("info", "attempted to remove a poltergeist with no presence")
201
-      return;
202
-   end
203
-
204
-   local leave_presence = st.clone(current_presence)
205
-   leave_presence.attr.to = room.jid.."/"..nick;
206
-   leave_presence.attr.from = poltergeist_component.."/"..nick;
207
-   leave_presence.attr.type = "unavailable";
208
-
209
-   if (ignore) then
210
-      poltergeists_pr_ignore[room.jid.."/"..nick] = true;
211
-   end
212
-
213
-   room:handle_normal_presence(
214
-      prosody.hosts[poltergeist_component], leave_presence);
215
-   remove_username(room, nick);
216
-end
217
-
218
-function update_poltergeist_occupant_status(room, nick, status, call_details)
219
-    local update_presence = get_presence(room, nick);
220
-
221
-    if (not update_presence) then
222
-       -- TODO: determine if we should provide an error and how that would be
223
-       -- handled for bosh and http api.
224
-       module:log("info", "update issued for a non-existing poltergeist")
225
-       return;
226
-    end
227
-
228
-    -- update occupant presence with appropriate to and from
229
-    -- so we can send it again
230
-    update_presence = st.clone(update_presence);
231
-    update_presence.attr.to = room.jid.."/"..nick;
232
-    update_presence.attr.from = poltergeist_component.."/"..nick;
233
-
234
-    update_presence = update_presence_tags(update_presence, status, call_details)
235
-
236
-    room:handle_normal_presence(
237
-        prosody.hosts[poltergeist_component], update_presence);
238
-end
239
-
240
-function update_presence_tags(presence_stanza, status, call_details)
241
-    local call_cancel = false;
242
-    local call_id = nil;
243
-
244
-    -- Extract optional call flow signal information.
245
-    if call_details then
246
-        call_id = call_details["id"];
247
-
248
-        if call_details["cancel"] then
249
-            call_cancel = call_details["cancel"];
250
-        end
251
-    end
252
-
253
-    presence_stanza:maptags(function (tag)
254
-        if tag.name == "status" then
255
-            if call_cancel then
256
-                -- If call cancel is set then the status should not be changed.
257
-                return tag
258
-            end
259
-            return st.stanza("status"):text(status);
260
-        elseif tag.name == "call_id" then
261
-            if call_id then
262
-                return st.stanza("call_id"):text(call_id);
263
-            else
264
-                -- If no call id is provided the re-use the existing id.
265
-                return tag;
266
-            end
267
-        elseif tag.name == "call_cancel" then
268
-            if call_cancel then
269
-                return st.stanza("call_cancel"):text("true");
270
-            else
271
-                return st.stanza("call_cancel"):text("false");
272
-            end
273
-        end
274
-        return tag;
275
-    end);
276
-
277
-    return presence_stanza
278
-end
279
-
280
-function have_poltergeist_occupant(room, nick)
281
-	-- Find out if we have a poltergeist occupant in the room for this JID
282
-	return not not room:get_occupant_jid(poltergeist_component.."/"..nick);
283
-end
284
-
285
-function get_presence(room, nick)
286
-    local occupant_jid
287
-        = room:get_occupant_jid(poltergeist_component.."/"..nick);
288
-    if (occupant_jid) then
289
-        return room:get_occupant_by_nick(occupant_jid):get_presence();
290
-    end
291
-
292
-    return nil;
293
-end
294
-
295
-
296
 --- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
138
 --- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
297
 --- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
139
 --- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
298
 module:hook("muc-decline", function (event)
140
 module:hook("muc-decline", function (event)
299
-	remove_poltergeist_occupant(event.room, bare(event.stanza.attr.from), false);
141
+    poltergeist.remove(event.room, bare(event.stanza.attr.from), false);
300
 end, -100);
142
 end, -100);
301
 -- before sending the presence for a poltergeist leaving add ignore tag
143
 -- before sending the presence for a poltergeist leaving add ignore tag
302
 -- as poltergeist is leaving just before the real user joins and in the client
144
 -- as poltergeist is leaving just before the real user joins and in the client
328
 module:hook("muc-broadcast-presence", function (event)
147
 module:hook("muc-broadcast-presence", function (event)
329
     if (bare(event.occupant.jid) == poltergeist_component) then
148
     if (bare(event.occupant.jid) == poltergeist_component) then
330
         if(event.stanza.attr.type == "unavailable"
149
         if(event.stanza.attr.type == "unavailable"
331
-            and poltergeists_pr_ignore[event.occupant.nick]) then
150
+        and poltergeist.should_ignore(event.occupant.nick)) then
332
             event.stanza:tag(
151
             event.stanza:tag(
333
                 "ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
152
                 "ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
334
-            poltergeists_pr_ignore[event.occupant.nick] = nil;
153
+                poltergeist.reset_ignored(event.occupant.nick);
335
         end
154
         end
336
     end
155
     end
337
 end, -100);
156
 end, -100);
338
 
157
 
339
 -- cleanup room table after room is destroyed
158
 -- cleanup room table after room is destroyed
340
-module:hook("muc-room-destroyed",function(event)
341
-    local room_name = jid.node(event.room.jid);
342
-    if (poltergeists[room_name]) then
343
-        poltergeists[room_name] = nil;
344
-    end
345
-end);
159
+module:hook(
160
+   "muc-room-destroyed",
161
+   function(event)
162
+      poltergeist.remove_room(event.room);
163
+   end
164
+);
346
 
165
 
347
 --- Handles request for creating/managing poltergeists
166
 --- Handles request for creating/managing poltergeists
348
 -- @param event the http event, holds the request query
167
 -- @param event the http event, holds the request query
375
 
194
 
376
     -- If the poltergiest is already in the conference then it will
195
     -- If the poltergiest is already in the conference then it will
377
     -- be in our username store and another can't be added.
196
     -- be in our username store and another can't be added.
378
-    local username = get_username(room, user_id);
379
-    if (username ~= nil
380
-        and have_poltergeist_occupant(room, string.sub(username, 0, 8))) then
381
-        log("warn", "poltergeist for username:%s already in the room:%s",
382
-            username, room_name);
197
+    local username = poltergeist.get_username(room, user_id);
198
+    if (username ~=nil and
199
+        poltergeist.occupies(room, poltergeist.create_nick(username))) then
200
+        log("warn",
201
+            "poltergeist for username:%s already in the room:%s",
202
+            username,
203
+            room_name
204
+        );
383
         return 202;
205
         return 202;
384
     end
206
     end
385
-    username = generate_uuid();
207
+
386
     local context = {
208
     local context = {
387
        user = {
209
        user = {
388
-	  id = user_id;
210
+           id = user_id;
389
        };
211
        };
390
        group = group;
212
        group = group;
391
        creator_user = session.jitsi_meet_context_user;
213
        creator_user = session.jitsi_meet_context_user;
392
        creator_group = session.jitsi_meet_context_group;
214
        creator_group = session.jitsi_meet_context_group;
393
     };
215
     };
394
-
395
-    local nick = string.sub(username, 0, 8)
396
-    local presence_stanza = original_presence(
397
-       poltergeist_component,
398
-       room,
399
-       nick,
400
-       name,
401
-       avatar,
402
-       username,
403
-       context,
404
-       status
405
-    )
406
-    store_username(room, user_id, username);
407
-
408
-    room:handle_first_presence(
409
-       prosody.hosts[poltergeist_component],
410
-       presence_stanza
411
-    );
412
-
413
-    -- the timeout before removing so participants can see the status update
414
-    local removeTimeout = 5;
415
-    local timeout = poltergeist_timeout - removeTimeout;
416
-
417
-    timer.add_task(timeout,
418
-        function ()
419
-            update_poltergeist_occupant_status(
420
-                room, nick, expired_status);
421
-            -- and remove it after some time so participant can see
422
-            -- the update
423
-            timer.add_task(removeTimeout,
424
-                function ()
425
-                    if (have_poltergeist_occupant(room, nick)) then
426
-                        remove_poltergeist_occupant(room, nick, false);
427
-                    end
428
-                end);
429
-        end);
430
-
216
+    poltergeist.add_to_muc(room, user_id, name, avatar, context, status)
431
     return 200;
217
     return 200;
432
 end
218
 end
433
 
219
 
434
-function original_presence(
435
-	component, room, nick, name, avatar, username, context, status)
436
-    local p = st.presence({
437
-	    to = room.jid.."/"..nick,
438
-	    from = component.."/"..nick,
439
-   }):tag("x", { xmlns = MUC_NS }):up();
440
-
441
-    p:tag("bot", { type = "poltergeist" }):up();
442
-    p:tag("call_cancel"):text(nil):up();
443
-    p:tag("call_id"):text(username):up();
444
-
445
-   if status then
446
-       p:tag("status"):text(status):up();
447
-   else
448
-       p:tag("status"):text(nil):up();
449
-   end
450
-
451
-   if (name) then
452
-       p:tag(
453
-	   "nick",
454
-	   { xmlns = "http://jabber.org/protocol/nick" }):text(name):up();
455
-   end
456
-
457
-   if (avatar) then
458
-       p:tag("avatar-url"):text(avatar):up();
459
-   end
460
-
461
-   -- If the room has a password set, let the poltergeist enter using it
462
-   local room_password = room:get_password();
463
-   if room_password then
464
-       local join = p:get_child("x", MUC_NS);
465
-       join:tag("password", { xmlns = MUC_NS }):text(room_password);
466
-   end
467
-
468
-   update_presence_identity(
469
-       p,
470
-       context.user,
471
-       context.group,
472
-       context.creator_user,
473
-       context.creator_group
474
-   );
475
-   return p
476
-end
477
-
478
 --- Handles request for updating poltergeists status
220
 --- Handles request for updating poltergeists status
479
 -- @param event the http event, holds the request query
221
 -- @param event the http event, holds the request query
480
 -- @return GET response, containing a json with response details
222
 -- @return GET response, containing a json with response details
514
         return 404;
247
         return 404;
515
     end
248
     end
516
 
249
 
517
-    local username = get_username(room, user_id);
250
+    local username = poltergeist.get_username(room, user_id);
518
     if (not username) then
251
     if (not username) then
519
         return 404;
252
         return 404;
520
     end
253
     end
521
 
254
 
522
-	local call_details = {
523
-	   ["cancel"] = call_cancel;
524
-	   ["id"] = call_id;
525
-	};
255
+    local call_details = {
256
+        ["cancel"] = call_cancel;
257
+        ["id"] = call_id;
258
+    };
526
 
259
 
527
-    local nick = string.sub(username, 0, 8);
528
-    if (not have_poltergeist_occupant(room, nick)) then
260
+    local nick = poltergeist.create_nick(username);
261
+    if (not poltergeist.occupies(room, nick)) then
529
        return 404;
262
        return 404;
530
     end
263
     end
531
 
264
 
532
-    update_poltergeist_occupant_status(room, nick, status, call_details);
265
+    poltergeist.update(room, nick, status, call_details);
533
     return 200;
266
     return 200;
534
 end
267
 end
535
 
268
 
556
         return 404;
289
         return 404;
557
     end
290
     end
558
 
291
 
559
-    local username = get_username(room, user_id);
292
+    local username = poltergeist.get_username(room, user_id);
560
     if (not username) then
293
     if (not username) then
561
         return 404;
294
         return 404;
562
     end
295
     end
563
 
296
 
564
-    local nick = string.sub(username, 0, 8);
565
-    if (not have_poltergeist_occupant(room, nick)) then
297
+    local nick = poltergeist.create_nick(username);
298
+    if (not poltergeist.occupies(room, nick)) then
566
        return 404;
299
        return 404;
567
     end
300
     end
568
 
301
 
569
-    remove_poltergeist_occupant(room, nick, false);
302
+    poltergeist.remove(room, nick, false);
570
     return 200;
303
     return 200;
571
 end
304
 end
572
 
305
 

+ 372
- 0
resources/prosody-plugins/poltergeist.lib.lua Zobrazit soubor

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 existance 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
+--------------------------------------------------------------------------------
54
+-- Username storage for poltergeist.
55
+--
56
+-- Every poltergeist will have a username stored in a table underneath
57
+-- the room name that they are currently active in. The username can
58
+-- be retrieved given a room and a user_id. The username is removed from
59
+-- a room by providing the room and the nick.
60
+--
61
+-- A table with a single entry looks like:
62
+-- {
63
+--   ["[hug]hostilewerewolvesthinkslightly"] = {
64
+--     ["655363:52148a3e-b5fb-4cfc-8fbd-f55e793cf657"] = "ed7757d6-d88d-4e6a-8e24-aca2adc31348",
65
+--     ed7757d6 = "655363:52148a3e-b5fb-4cfc-8fbd-f55e793cf657"
66
+--   }
67
+-- }
68
+--------------------------------------------------------------------------------
69
+-- username is the table where all poltergeist are stored
70
+local usernames = {}
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 usernames[room_name] then
80
+        usernames[room_name] = {}
81
+    end
82
+
83
+    usernames[room_name][user_id] = username
84
+    usernames[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 usernames[room_name] then
94
+        return nil
95
+    end
96
+
97
+    return usernames[room_name][user_id]
98
+end
99
+
100
+local function get_username_from_nick(room_name, nick)
101
+    if not usernames[room_name] then
102
+        return nil
103
+    end
104
+
105
+    local user_id = usernames[room_name][nick]
106
+    return usernames[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 usernames[room_name] then
115
+        return
116
+    end
117
+
118
+    local user_id = usernames[room_name][nick]
119
+    usernames[room_name][user_id] = nil
120
+    usernames[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 usernames[room_name] then
128
+        usernames[room_name] = nil
129
+    end
130
+end
131
+
132
+--------------------------------------------------------------------------------
133
+-- State for toggling the tagging of presence stanzas with ignored tag.
134
+--
135
+-- A poltergeist with it's full room/nick set to ignore will have a jitsi ignore
136
+-- tag applied to all presence stanza's broadcasted. The following funcitons
137
+-- assisst in managing this state.
138
+--------------------------------------------------------------------------------
139
+local presence_ignored = {}
140
+
141
+-- Sets the nick to ignored state.
142
+-- @param room_nick full room/nick jid
143
+local function set_ignored(room_nick)
144
+    presence_ignored[room_nick] = true
145
+end
146
+
147
+-- Resets the nick out of ignored state.
148
+-- @param room_nick full room/nick jid
149
+local function reset_ignored(room_nick)
150
+    presence_ignored[room_nick] = nil
151
+end
152
+
153
+-- Determines whether or not the leave presence should be tagged with ignored.
154
+-- @param room_nick full room/nick jid
155
+local function should_ignore(room_nick)
156
+    if presence_ignored[room_nick] == nil then
157
+        return false
158
+    end
159
+    return presence_ignored[room_nick]
160
+end
161
+
162
+--------------------------------------------------------------------------------
163
+-- Poltergeist control functions for adding, updating and removing poltergeist.
164
+--------------------------------------------------------------------------------
165
+
166
+-- Updates the status tags and call flow tags of an existing poltergeist
167
+-- presence.
168
+-- @param presence_stanza is the actual presence stanza for a poltergeist.
169
+-- @param status is the new status to be updated in the stanza.
170
+-- @param call_details is a table of call flow signal information.
171
+function update_presence_tags(presence_stanza, status, call_details)
172
+    local call_cancel = false
173
+    local call_id = nil
174
+
175
+    -- Extract optional call flow signal information.
176
+    if call_details then
177
+        call_id = call_details["id"]
178
+
179
+        if call_details["cancel"] then
180
+            call_cancel = call_details["cancel"]
181
+        end
182
+    end
183
+
184
+    presence_stanza:maptags(function (tag)
185
+        if tag.name == "status" then
186
+            if call_cancel then
187
+                -- If call cancel is set then the status should not be changed.
188
+                return tag
189
+            end
190
+            return stanza.stanza("status"):text(status)
191
+        elseif tag.name == "call_id" then
192
+            if call_id then
193
+                return stanza.stanza("call_id"):text(call_id)
194
+            else
195
+                -- If no call id is provided the re-use the existing id.
196
+                return tag
197
+            end
198
+        elseif tag.name == "call_cancel" then
199
+            if call_cancel then
200
+                return stanza.stanza("call_cancel"):text("true")
201
+            else
202
+                return stanza.stanza("call_cancel"):text("false")
203
+            end
204
+        end
205
+        return tag
206
+    end)
207
+
208
+    return presence_stanza
209
+end
210
+
211
+-- Updates the presence status of a poltergeist.
212
+-- @param room is the room the poltergeist has occupied
213
+-- @param nick is the xmpp nick of the poltergeist occupant
214
+-- @param status is the status string to set in the presence
215
+-- @param call_details is a table of call flow control details
216
+local function update(room, nick, status, call_details)
217
+    local original_presence = get_presence(room, nick)
218
+
219
+    if not original_presence then
220
+        module:log("info", "update issued for a non-existing poltergeist")
221
+        return
222
+    end
223
+
224
+    -- update occupant presence with appropriate to and from
225
+    -- so we can send it again
226
+    update_presence = stanza.clone(original_presence)
227
+    update_presence.attr.to = room.jid.."/"..nick
228
+    update_presence.attr.from = component.."/"..nick
229
+
230
+    update_presence = update_presence_tags(update_presence, status, call_details)
231
+
232
+    module:log("info", "updating poltergeist: %s/%s - %s", room, nick, status)
233
+    room:handle_normal_presence(
234
+        prosody.hosts[component],
235
+        update_presence
236
+    )
237
+end
238
+
239
+-- Removes the poltergeist from the room.
240
+-- @param room is the room the poltergeist has occupied
241
+-- @param nick is the xmpp nick of the poltergeist occupant
242
+-- @param ignore toggles if the leave subsequent leave presence should be tagged
243
+local function remove(room, nick, ignore)
244
+    local original_presence = get_presence(room, nick);
245
+    if not original_presence then
246
+        module:log("info", "attempted to remove a poltergeist with no presence")
247
+        return
248
+    end
249
+
250
+    local leave_presence = stanza.clone(original_presence)
251
+    leave_presence.attr.to = room.jid.."/"..nick
252
+    leave_presence.attr.from = component.."/"..nick
253
+    leave_presence.attr.type = "unavailable"
254
+
255
+    if (ignore) then
256
+        set_ignored(room, nick)
257
+    end
258
+
259
+    remove_username(room, nick)
260
+    module:log("info", "removing poltergeist: %s/%s", room, nick)
261
+    room:handle_normal_presence(
262
+        prosody.hosts[component],
263
+        leave_presence
264
+    )
265
+end
266
+
267
+-- Adds a poltergeist to a muc/room.
268
+-- @param room is the room the poltergeist will occupy
269
+-- @param is the id of the user the poltergeist represents
270
+-- @param display_name is the display name to use for the poltergeist
271
+-- @param avatar is the avatar link used for the poltergeist display
272
+-- @param context is the session context of the user making the request
273
+-- @param status is the presence status string to use
274
+local function add_to_muc(room, user_id, display_name, avatar, context, status)
275
+    local username = uuid.generate()
276
+    local presence_stanza = original_presence(
277
+        room,
278
+        username,
279
+        display_name,
280
+        avatar,
281
+        context,
282
+        status
283
+    )
284
+    store_username(room, user_id, username)
285
+    module:log("info", "adding poltergeist: %s/%s", room, create_nick(username))
286
+    room:handle_first_presence(
287
+        prosody.hosts[component],
288
+        presence_stanza
289
+    )
290
+    local remove_delay = 5
291
+    local expiration = expiration_timeout - remove_delay;
292
+    local nick = create_nick(username)
293
+    timer.add_task(
294
+        expiration,
295
+        function ()
296
+            update(room, nick, "expired")
297
+            timer.add_task(
298
+                remove_delay,
299
+                function ()
300
+                    if occupies(room, nick) then
301
+                        remove(room, nick, false)
302
+                    end
303
+                end
304
+            )
305
+        end
306
+    )
307
+end
308
+
309
+-- Generates an original presence for a new poltergeist
310
+-- @param room is the room the poltergeist will occupy
311
+-- @param username is the unique name for the poltergeist
312
+-- @param display_name is the display name to use for the poltergeist
313
+-- @param avatar is the avatar link used for the poltergeist display
314
+-- @param context is the session context of the user making the request
315
+-- @param status is the presence status string to use
316
+-- @return a presence stanza that can be used to add the poltergeist to the muc
317
+function original_presence(room, username, display_name, avatar, context, status)
318
+    local nick = create_nick(username)
319
+    local p = stanza.presence({
320
+        to = room.jid.."/"..nick,
321
+        from = component.."/"..nick,
322
+    }):tag("x", { xmlns = MUC_NS }):up();
323
+
324
+    p:tag("bot", { type = "poltergeist" }):up();
325
+    p:tag("call_cancel"):text(nil):up();
326
+    p:tag("call_id"):text(username):up();
327
+
328
+    if status then
329
+        p:tag("status"):text(status):up();
330
+    else
331
+        p:tag("status"):text(nil):up();
332
+    end
333
+
334
+    if display_name then
335
+        p:tag(
336
+            "nick",
337
+            { xmlns = "http://jabber.org/protocol/nick" }):text(display_name):up();
338
+    end
339
+
340
+    if avatar then
341
+        p:tag("avatar-url"):text(avatar):up();
342
+    end
343
+
344
+    -- If the room has a password set, let the poltergeist enter using it
345
+    local room_password = room:get_password();
346
+    if room_password then
347
+        local join = p:get_child("x", MUC_NS);
348
+        join:tag("password", { xmlns = MUC_NS }):text(room_password);
349
+    end
350
+
351
+    update_presence_identity(
352
+        p,
353
+        context.user,
354
+        context.group,
355
+        context.creator_user,
356
+        context.creator_group
357
+    )
358
+    return p
359
+end
360
+
361
+return {
362
+    get_username = get_username,
363
+    get_username_from_nick = get_username_from_nick,
364
+    occupies = occupies,
365
+    remove_room = remove_room,
366
+    reset_ignored = reset_ignored,
367
+    should_ignore = should_ignore,
368
+    create_nick = create_nick,
369
+    add_to_muc = add_to_muc,
370
+    update = update,
371
+    remove = remove
372
+}

Načítá se…
Zrušit
Uložit