소스 검색

Preventing expired notification for poltergeist that have left.

The original presence stanza generation code for a poltergeist
has been re-factored and simplified a bit. Every time a
poltergeist presence is updated we first check that the poltergeist
still exists.
master
Jacob MacElroy 7 년 전
부모
커밋
0acc9187ed
2개의 변경된 파일221개의 추가작업 그리고 186개의 파일을 삭제
  1. 71
    59
      resources/prosody-plugins/mod_muc_call.lua
  2. 150
    127
      resources/prosody-plugins/mod_muc_poltergeist.lua

+ 71
- 59
resources/prosody-plugins/mod_muc_call.lua 파일 보기

@@ -3,16 +3,16 @@ local jid = require "util.jid"
3 3
 
4 4
 -- Options and configuration
5 5
 local poltergeist_component = module:get_option_string(
6
-   "poltergeist_component",
7
-   module.host
6
+    "poltergeist_component",
7
+    module.host
8 8
 );
9 9
 local muc_domain_base = module:get_option_string("muc_mapper_domain_base");
10 10
 if not muc_domain_base then
11
-   module:log(
12
-	  "warn",
13
-	  "No 'muc_domain_base' option set, unable to send call events."
14
-   );
15
-   return
11
+    module:log(
12
+        "warn",
13
+        "No 'muc_domain_base' option set, unable to send call events."
14
+    );
15
+    return
16 16
 end
17 17
 
18 18
 -- Status strings that trigger call events.
@@ -31,17 +31,17 @@ local connected_status = "connected"
31 31
 --     https://meet.jit.si/MoreProductiveMeeting
32 32
 -- The urls are derived from portions of the room jid.
33 33
 local function url_from_room_jid(room_jid)
34
-   local node, _, _ = jid.split(room_jid)
35
-   if not node then return nil end
34
+    local node, _, _ = jid.split(room_jid)
35
+    if not node then return nil end
36 36
 
37
-   local target_subdomain, target_node = node:match("^%[([^%]]+)%](.+)$")
37
+    local target_subdomain, target_node = node:match("^%[([^%]]+)%](.+)$")
38 38
 
39
-   if not(target_node or target_subdomain) then
40
-	  return "https://"..muc_domain_base.."/"..node
41
-   else
42
-	  return
43
-		 "https://"..muc_domain_base.."/"..target_subdomain.."/"..target_node
44
-   end
39
+    if not(target_node or target_subdomain) then
40
+        return "https://"..muc_domain_base.."/"..node
41
+    else
42
+        return
43
+        "https://"..muc_domain_base.."/"..target_subdomain.."/"..target_node
44
+    end
45 45
 end
46 46
 
47 47
 -- Listening for all muc presences stanza events. If a presence stanza is from
@@ -55,48 +55,60 @@ end
55 55
 --    "busy"      | CANCEL
56 56
 --    "rejected"  | CANCEL
57 57
 --    "connected" | CANCEL
58
-module:hook("muc-broadcast-presence", function (event)
59
-    -- Detect if the presence is for a poltergeist or not.
60
-    if not
61
-	   (jid.bare(event.occupant.jid) == poltergeist_component)
62
-	   or
63
-	   event.stanza == nil
64
-	then
65
-	   return
66
-    end
58
+module:hook(
59
+    "muc-broadcast-presence",
60
+    function (event)
61
+        -- Detect if the presence is for a poltergeist or not.
62
+        if not (jid.bare(event.occupant.jid) == poltergeist_component) then
63
+            return
64
+        end
67 65
 
68
-	local call_id = event.stanza:get_child_text("call_id")
69
-	if not call_id then
70
-	   module:log("info", "A call id was not provided in the status.")
71
-	   return
72
-	end
73
-
74
-    local invite = function()
75
-	    local url = assert(url_from_room_jid(event.stanza.attr.from))
76
-		ext_events.invite(event.stanza, url, call_id)
77
-	end
78
-
79
-    local cancel = function()
80
-	   local url = assert(url_from_room_jid(event.stanza.attr.from))
81
-	   local status = event.stanza:get_child_text("status")
82
-	   ext_events.cancel(event.stanza, url, string.lower(status), call_id)
83
-	end
84
-
85
-    local should_cancel = event.stanza:get_child_text("call_cancel")
86
-    if should_cancel == "true" then
87
-        cancel()
88
-        return
89
-    end
66
+        -- A presence stanza is needed in order to trigger any calls.
67
+        if not event.stanza then
68
+            return
69
+        end
70
+
71
+        local call_id = event.stanza:get_child_text("call_id")
72
+        if not call_id then
73
+            module:log("info", "A call id was not provided in the status.")
74
+            return
75
+        end
90 76
 
91
-	local switch = function(status)
92
-	   case = {
93
-		  [calling_status]   = function() invite() end,
94
-		  [busy_status]      = function() cancel() end,
95
-		  [rejected_status]  = function() cancel() end,
96
-		  [connected_status] = function() cancel() end
97
-	   }
98
-	   if case[status] then case[status]() end
99
-	end
100
-
101
-	switch(event.stanza:get_child_text("status"))
102
-end, -101);
77
+        local invite = function()
78
+            local url = assert(url_from_room_jid(event.stanza.attr.from))
79
+            ext_events.invite(event.stanza, url, call_id)
80
+        end
81
+
82
+        local cancel = function()
83
+            local url = assert(url_from_room_jid(event.stanza.attr.from))
84
+            local status = event.stanza:get_child_text("status")
85
+            ext_events.cancel(event.stanza, url, string.lower(status), call_id)
86
+        end
87
+
88
+        -- If for any reason call_cancel is set to true then a cancel
89
+        -- is sent regardless of the rest of the presence info.
90
+        local should_cancel = event.stanza:get_child_text("call_cancel")
91
+        if should_cancel == "true" then
92
+            cancel()
93
+            return
94
+        end
95
+
96
+        -- All other call flow actions will require a status.
97
+        if event.stanza:get_child_text("status") == nil then
98
+            return
99
+        end
100
+
101
+        local switch = function(status)
102
+            case = {
103
+                [calling_status]   = function() invite() end,
104
+                [busy_status]      = function() cancel() end,
105
+                [rejected_status]  = function() cancel() end,
106
+                [connected_status] = function() cancel() end
107
+            }
108
+            if case[status] then case[status]() end
109
+        end
110
+
111
+        switch(event.stanza:get_child_text("status"))
112
+    end,
113
+    -101
114
+);

+ 150
- 127
resources/prosody-plugins/mod_muc_poltergeist.lua 파일 보기

@@ -14,9 +14,9 @@ local expired_status  = "expired";
14 14
 -- Options
15 15
 local poltergeist_component
16 16
     = module:get_option_string("poltergeist_component", module.host);
17
+-- defaults to 30 seconds
17 18
 local poltergeist_timeout
18
-    = module:get_option_string("poltergeist_leave_timeout", 180);
19
+    = module:get_option_string("poltergeist_leave_timeout", 30);
19 20
 -- this basically strips the domain from the conference.domain address
20 21
 local parentHostName = string.gmatch(tostring(module.host), "%w+.(%w.+)")();
21 22
 if parentHostName == nil then
@@ -108,27 +108,6 @@ function remove_username(room, nick)
108 108
     end
109 109
 end
110 110
 
111
-function generate_poltergeist_presence(room, nick, status)
112
-    local presence_stanza = st.presence({
113
-        to = room.jid.."/"..nick,
114
-        from = poltergeist_component.."/"..nick,
115
-    }):tag("x", { xmlns = MUC_NS }):up();
116
-
117
-    presence_stanza:tag("call_cancel"):text(nil):up();
118
-    presence_stanza:tag("call_id"):text(nil):up();
119
-
120
-    if status then
121
-       presence_stanza:tag("status"):text(status):up();
122
-    else
123
-       presence_stanza:tag("status"):text(nil):up();
124
-    end
125
-
126
-    return presence_stanza;
127
-end
128
-
129 111
 --- Verifies room name, domain name with the values in the token
130 112
 -- @param token the token we received
131 113
 -- @param room_name the room name
@@ -213,89 +192,31 @@ prosody.events.add_handler("pre-jitsi-authentication", function(session)
213 192
     return nil;
214 193
 end);
215 194
 
216
-function create_poltergeist_occupant(room, nick, name, avatar, status, context)
217
-    log("debug", "create_poltergeist_occupant %s", nick);
218
-    -- Join poltergeist occupant to room, with the invited JID as their nick
219
-    local join_presence = generate_poltergeist_presence(room, nick, status)
220
-
221
-    if (name) then
222
-        join_presence:tag(
223
-            "nick",
224
-            { xmlns = "http://jabber.org/protocol/nick" }):text(name):up();
225
-    end
226
-    if (avatar) then
227
-        join_presence:tag("avatar-url"):text(avatar):up();
228
-    end
229
-
230
-    -- If the room has a password set, let the poltergeist enter using it
231
-    local room_password = room:get_password();
232
-    if room_password then
233
-        local join = join_presence:get_child("x", MUC_NS);
234
-        join:tag("password", { xmlns = MUC_NS }):text(room_password);
235
-    end
236
-
237
-    -- Update the nil call id to the initial call id for call flows.
238
-	local call_id = get_username(room, context.user.id);
239
-    join_presence:maptags(function (tag)
240
-        if tag.name == "call_id" then
241
-           return st.stanza("call_id"):text(call_id);
242
-        end
243
-        return tag;
244
-    end);
245
-
246
-    update_presence_identity(
247
-        join_presence,
248
-        context.user,
249
-        context.group,
250
-        context.creator_user,
251
-        context.creator_group
252
-    );
253
-
254
-    room:handle_first_presence(
255
-        prosody.hosts[poltergeist_component], join_presence);
256
-
257
-    -- the timeout before removing so participants can see the status update
258
-    local removeTimeout = 5;
259
-    local timeout = poltergeist_timeout - removeTimeout;
260
-
261
-    timer.add_task(timeout,
262
-        function ()
263
-            update_poltergeist_occupant_status(
264
-                room, nick, expired_status);
265
-            -- and remove it after some time so participant can see
266
-            -- the update
267
-            timer.add_task(removeTimeout,
268
-                function ()
269
-                    if (have_poltergeist_occupant(room, nick)) then
270
-                        remove_poltergeist_occupant(room, nick, false);
271
-                    end
272
-                end);
273
-        end);
274
-end
275
-
276 195
 -- Removes poltergeist occupant
277 196
 -- @param room the room instance where to remove the occupant
278 197
 -- @param nick the nick of the occupant to remove
279 198
 -- @param ignore to mark the poltergeist unavailble presence to be ignored
280 199
 function remove_poltergeist_occupant(room, nick, ignore)
281
-    log("debug", "remove_poltergeist_occupant %s", nick);
282
-    local leave_presence = st.presence({
283
-        to = room.jid.."/"..nick,
284
-        from = poltergeist_component.."/"..nick,
285
-        type = "unavailable" });
286
-    if (ignore) then
287
-        poltergeists_pr_ignore[room.jid.."/"..nick] = true;
288
-    end
289
-    room:handle_normal_presence(
290
-        prosody.hosts[poltergeist_component], leave_presence);
291
-    remove_username(room, nick);
200
+   log("debug", "remove_poltergeist_occupant %s", nick);
201
+
202
+   local current_presence = get_presence(room, nick);
203
+   if (not current_presence) then
204
+      module:log("info", "attempted to remove a poltergeist with no presence")
205
+      return;
206
+   end
207
+
208
+   local leave_presence = st.clone(current_presence)
209
+   leave_presence.attr.to = room.jid.."/"..nick;
210
+   leave_presence.attr.from = poltergeist_component.."/"..nick;
211
+   leave_presence.attr.type = "unavailable";
212
+
213
+   if (ignore) then
214
+      poltergeists_pr_ignore[room.jid.."/"..nick] = true;
215
+   end
216
+
217
+   room:handle_normal_presence(
218
+      prosody.hosts[poltergeist_component], leave_presence);
219
+   remove_username(room, nick);
292 220
 end
293 221
 
294 222
 -- Updates poltergeist occupant status
@@ -307,16 +228,18 @@ function update_poltergeist_occupant_status(room, nick, status, call_details)
307 228
     local update_presence = get_presence(room, nick);
308 229
 
309 230
     if (not update_presence) then
310
-        -- no presence found for occupant, create one
311
-        update_presence = generate_poltergeist_presence(room, nick)
312
-    else
313
-        -- update occupant presence with appropriate to and from
314
-        -- so we can send it again
315
-        update_presence = st.clone(update_presence);
316
-        update_presence.attr.to = room.jid.."/"..nick;
317
-        update_presence.attr.from = poltergeist_component.."/"..nick;
231
+       -- TODO: determine if we should provide an error and how that would be
232
+       -- handled for bosh and http api.
233
+       module:log("info", "update issued for a non-existing poltergeist")
234
+       return;
318 235
     end
319 236
 
237
+    -- update occupant presence with appropriate to and from
238
+    -- so we can send it again
239
+    update_presence = st.clone(update_presence);
240
+    update_presence.attr.to = room.jid.."/"..nick;
241
+    update_presence.attr.from = poltergeist_component.."/"..nick;
242
+
320 243
     update_presence = update_presence_tags(update_presence, status, call_details)
321 244
 
322 245
     room:handle_normal_presence(
@@ -442,33 +365,122 @@ function handle_create_poltergeist (event)
442 365
         return 403;
443 366
     end
444 367
 
368
+    -- If the provided room conference doesn't exist then we
369
+    -- can't add a poltergeist to it.
445 370
     local room = get_room(room_name, group);
446 371
     if (not room) then
447 372
         log("error", "no room found %s", room_name);
448 373
         return 404;
449 374
     end
450 375
 
376
+    -- If the poltergiest is already in the conference then it will
377
+    -- be in our username store and another can't be added.
451 378
     local username = get_username(room, user_id);
452 379
     if (username ~= nil
453 380
         and have_poltergeist_occupant(room, string.sub(username, 0, 8))) then
454 381
         log("warn", "poltergeist for username:%s already in the room:%s",
455 382
             username, room_name);
456 383
         return 202;
457
-    else
458
-        username = generate_uuid();
459
-        store_username(room, user_id, username);
460
-        local context = {
461
-            user = {
462
-                id = user_id;
463
-            };
464
-            group = group;
465
-            creator_user = session.jitsi_meet_context_user;
466
-            creator_group = session.jitsi_meet_context_group;
467
-        };
468
-        create_poltergeist_occupant(
469
-            room, string.sub(username, 0, 8), name, avatar, status, context);
470
-        return 200;
471 384
     end
385
+    username = generate_uuid();
386
+    local context = {
387
+       user = {
388
+	  id = user_id;
389
+       };
390
+       group = group;
391
+       creator_user = session.jitsi_meet_context_user;
392
+       creator_group = session.jitsi_meet_context_group;
393
+    };
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
+
431
+    return 200;
432
+end
433
+
434
+-- Generate the original presence for a poltergeist when it is added to a room.
435
+-- @param component is the configured component name for poltergeist.
436
+-- @param room is the room the poltergeist is being added to.
437
+-- @param nick is the nick the poltergeist will use for xmpp.
438
+-- @param avatar is the url of the display avatar for the poltergeist.
439
+-- @param username is the poltergeist unique username.
440
+-- @param context is the context information from the valid auth token.
441
+-- @param status is the status string for the presence.
442
+-- @return a presence stanza
443
+function original_presence(
444
+	component, room, nick, name, avatar, username, context, status)
445
+    local p = st.presence({
446
+	    to = room.jid.."/"..nick,
447
+	    from = component.."/"..nick,
448
+   }):tag("x", { xmlns = MUC_NS }):up();
449
+
450
+    p:tag("call_cancel"):text(nil):up();
451
+    p:tag("call_id"):text(username):up();
452
+
453
+   if status then
454
+       p:tag("status"):text(status):up();
455
+   else
456
+       p:tag("status"):text(nil):up();
457
+   end
458
+
459
+   if (name) then
460
+       p:tag(
461
+	   "nick",
462
+	   { xmlns = "http://jabber.org/protocol/nick" }):text(name):up();
463
+   end
464
+
465
+   if (avatar) then
466
+       p:tag("avatar-url"):text(avatar):up();
467
+   end
468
+
469
+   -- If the room has a password set, let the poltergeist enter using it
470
+   local room_password = room:get_password();
471
+   if room_password then
472
+       local join = p:get_child("x", MUC_NS);
473
+       join:tag("password", { xmlns = MUC_NS }):text(room_password);
474
+   end
475
+
476
+   update_presence_identity(
477
+       p,
478
+       context.user,
479
+       context.group,
480
+       context.creator_user,
481
+       context.creator_group
482
+   );
483
+   return p
472 484
 end
473 485
 
474 486
 --- Handles request for updating poltergeists status
@@ -484,12 +496,12 @@ function handle_update_poltergeist (event)
484 496
     local room_name = params["room"];
485 497
     local group = params["group"];
486 498
     local status = params["status"];
487
-	local call_id = params["callid"];
499
+    local call_id = params["callid"];
488 500
 
489
-	local call_cancel = false
490
-	if params["callcancel"] == "true" then
491
-	   call_cancel = true;
492
-	end
501
+    local call_cancel = false
502
+    if params["callcancel"] == "true" then
503
+       call_cancel = true;
504
+    end
493 505
 
494 506
     if not verify_token(params["token"], room_name, group, {}) then
495 507
         return 403;
@@ -512,12 +524,12 @@ function handle_update_poltergeist (event)
512 524
 	};
513 525
 
514 526
     local nick = string.sub(username, 0, 8);
515
-    if (have_poltergeist_occupant(room, nick)) then
516
-        update_poltergeist_occupant_status(room, nick, status, call_details);
517
-        return 200;
518
-    else
519
-        return 404;
527
+    if (not have_poltergeist_occupant(room, nick)) then
528
+       return 404;
520 529
     end
530
+
531
+    update_poltergeist_occupant_status(room, nick, status, call_details);
532
+    return 200;
521 533
 end
522 534
 
523 535
 --- Handles remove poltergeists
@@ -549,12 +561,12 @@ function handle_remove_poltergeist (event)
549 561
     end
550 562
 
551 563
     local nick = string.sub(username, 0, 8);
552
-    if (have_poltergeist_occupant(room, nick)) then
553
-        remove_poltergeist_occupant(room, nick, false);
554
-        return 200;
555
-    else
556
-        return 404;
564
+    if (not have_poltergeist_occupant(room, nick)) then
565
+       return 404;
557 566
     end
567
+
568
+    remove_poltergeist_occupant(room, nick, false);
569
+    return 200;
558 570
 end
559 571
 
560 572
 log("info", "Loading poltergeist service");

Loading…
취소
저장