Browse Source

Initial Lobby backend implementation.

j8
damencho 5 years ago
parent
commit
eea8fef044

+ 18
- 6
resources/prosody-plugins/mod_muc_allowners.lua View File

@@ -1,13 +1,21 @@
1
---
1
+local is_healthcheck_room = module:require "util".is_healthcheck_room;
2 2
 
3
-local muc_service = module:depends("muc");
4
-local room_mt = muc_service.room_mt;
3
+module:hook("muc-occupant-joined", function (event)
4
+    local room, occupant = event.room, event.occupant;
5 5
 
6
+    if is_healthcheck_room(room) then
7
+        return;
8
+    end
6 9
 
7
-room_mt.get_affiliation = function (room, jid)
8
-    return "owner";
9
-end
10
+    room:set_affiliation(true, occupant.bare_jid, "owner");
11
+end, 2);
12
+
13
+module:hook("muc-occupant-left", function (event)
14
+    local room, occupant = event.room, event.occupant;
15
+
16
+    if is_healthcheck_room(room) then
17
+        return;
18
+    end
19
+
20
+    room:set_affiliation(true, occupant.bare_jid, nil);
21
+end, 2);

+ 245
- 0
resources/prosody-plugins/mod_muc_lobby_rooms.lua View File

@@ -0,0 +1,245 @@
1
+-- This module added under the main virtual host domain
2
+-- It needs a lobby muc component
3
+--
4
+-- VirtualHost "jitmeet.example.com"
5
+-- modules_enabled = {
6
+--     "muc_lobby_rooms"
7
+-- }
8
+-- lobby_muc = "lobby.jitmeet.example.com"
9
+-- main_muc = "conference.jitmeet.example.com"
10
+--
11
+-- Component "lobbyrooms.damencho.jitsi.net" "muc"
12
+--     storage = "memory"
13
+--     muc_room_cache_size = 1000
14
+--     restrict_room_creation = true
15
+--     muc_room_locking = false
16
+--     muc_room_default_public_jids = true
17
+--
18
+-- we use async to detect Prosody 0.10 and earlier
19
+local have_async = pcall(require, "util.async");
20
+
21
+if not have_async then
22
+    module:log("warn", "Lobby rooms will not work with Prosody version 0.10 or less.");
23
+    return;
24
+end
25
+
26
+local jid_split = require 'util.jid'.split;
27
+local jid_bare = require 'util.jid'.bare;
28
+local filters = require 'util.filters';
29
+local st = require 'util.stanza';
30
+local MUC_NS = 'http://jabber.org/protocol/muc';
31
+
32
+local is_healthcheck_room = module:require "util".is_healthcheck_room;
33
+
34
+local main_muc_component_config = module:get_option_string('main_muc');
35
+if main_muc_component_config == nil then
36
+    module:log('error', 'lobby not enabled missing main_muc config');
37
+    return ;
38
+end
39
+local lobby_muc_component_config = module:get_option_string('lobby_muc');
40
+if lobby_muc_component_config == nil then
41
+    module:log('error', 'lobby not enabled missing lobby_muc config');
42
+    return ;
43
+end
44
+
45
+local lobby_muc_service;
46
+local main_muc_service;
47
+
48
+-- Checks whether there is self-status 110 of the <x node
49
+function check_self_status(muc_x)
50
+    if not muc_x then
51
+        return false;
52
+    end
53
+
54
+    for status in muc_x:childtags('status') do
55
+        if status.attr.code == '110' then
56
+            return true;
57
+        end
58
+    end
59
+
60
+    return false;
61
+end
62
+
63
+function filter_stanza(stanza)
64
+    if not stanza.attr or not stanza.attr.from or not main_muc_service then
65
+        return stanza;
66
+    end
67
+    -- Allow self-presence (code=110)
68
+    local node, from_domain = jid_split(stanza.attr.from);
69
+
70
+    if from_domain == lobby_muc_component_config then
71
+        if stanza.name == 'presence' then
72
+            local muc_x = stanza:get_child('x', MUC_NS..'#user');
73
+
74
+            if muc_x and check_self_status(muc_x) then
75
+                return stanza;
76
+            end
77
+
78
+            -- check is an owner, only owners can receive the presence
79
+            local room = main_muc_service.get_room_from_jid(jid_bare(node .. '@' .. main_muc_component_config));
80
+            if room.get_affiliation(room, stanza.attr.to) == 'owner' then
81
+                return stanza;
82
+            end
83
+
84
+            return nil;
85
+        end
86
+
87
+        return nil;
88
+    else
89
+        return stanza;
90
+    end
91
+end
92
+function filter_session(session)
93
+    if session.host and session.host == module.host then
94
+        -- domain mapper is filtering on default priority 0, and we need it after that
95
+        filters.add_filter(session, 'stanzas/out', filter_stanza, -1);
96
+    end
97
+end
98
+
99
+-- process a host module directly if loaded or hooks to wait for its load
100
+function process_host_module(name, callback)
101
+    local function process_host(host)
102
+        if host == name then
103
+            callback(module:context(host), host);
104
+        end
105
+    end
106
+
107
+    if prosody.hosts[name] == nil then
108
+        module:log('debug', 'No host/component found, will wait for it: %s', name)
109
+
110
+        -- when a host or component is added
111
+        prosody.events.add_handler('host-activated', process_host);
112
+    else
113
+        process_host(name);
114
+    end
115
+end
116
+
117
+-- operates on already loaded lobby muc module
118
+function process_lobby_muc_loaded(lobby_muc, host_module)
119
+    module:log('debug', 'Lobby muc loaded');
120
+    lobby_muc_service = lobby_muc;
121
+
122
+    -- enable filtering presences in the lobby muc rooms
123
+    filters.add_filter_hook(filter_session);
124
+
125
+    -- Advertise lobbyrooms support on main domain so client can pick up the address and use it
126
+    module:add_identity('component', 'lobbyrooms', lobby_muc_component_config);
127
+
128
+    local room_mt = lobby_muc_service.room_mt;
129
+    -- we base affiliations (roles) in lobby muc component to be based on the roles in the main muc
130
+    room_mt.get_affiliation = function(room, jid)
131
+        if not room.main_room then
132
+            module:log('error', 'No main room(%s) for %s!', room.jid, jid);
133
+            return 'none';
134
+        end
135
+
136
+        -- moderators in main room are moderators here
137
+        local role = room.main_room.get_affiliation(room.main_room, jid);
138
+        if role then
139
+            return role;
140
+        end
141
+
142
+        return 'none';
143
+    end
144
+end
145
+
146
+-- process or waits to process the lobby muc component
147
+process_host_module(lobby_muc_component_config, function(host_module, host)
148
+    -- lobby muc component created
149
+    module:log('info', 'Lobby component loaded %s', host);
150
+
151
+    local muc_module = prosody.hosts[host].modules.muc;
152
+    if muc_module then
153
+        process_lobby_muc_loaded(muc_module, host_module);
154
+    else
155
+        module:log('debug', 'Will wait for muc to be available');
156
+        prosody.hosts[host].events.add_handler('module-loaded', function(event)
157
+            if (event.module == 'muc') then
158
+                process_lobby_muc_loaded(prosody.hosts[host].modules.muc, host_module);
159
+            end
160
+        end);
161
+    end
162
+end);
163
+
164
+-- process or waits to process the main muc component
165
+process_host_module(main_muc_component_config, function(host_module, host)
166
+    main_muc_service = prosody.hosts[host].modules.muc;
167
+
168
+    -- adds new field to the form so moderators can use it to set shared password
169
+    host_module:hook('muc-config-form', function(event)
170
+        table.insert(event.form, {
171
+            name = 'muc#roomconfig_lobbypassword';
172
+            type = 'text-private';
173
+            label = 'Shared Password';
174
+            value = '';
175
+        });
176
+    end, 90-4);
177
+
178
+    -- hooks when lobby is enabled to create its room, only done here or by admin
179
+    host_module:hook('muc-config-submitted', function(event)
180
+        local members_only = event.fields['muc#roomconfig_membersonly'] and true or nil;
181
+        if members_only then
182
+            local node = jid_split(event.room.jid);
183
+
184
+            local lobby_room_jid = node .. '@' .. lobby_muc_component_config;
185
+            if not lobby_muc_service.get_room_from_jid(lobby_room_jid) then
186
+                local new_room = lobby_muc_service.create_room(lobby_room_jid);
187
+                new_room.main_room = event.room;
188
+                event.room._data.lobbyroom = lobby_room_jid;
189
+                event.status_codes["104"] = true;
190
+
191
+                local lobby_password = event.fields['muc#roomconfig_lobbypassword'];
192
+                if lobby_password then
193
+                    new_room.main_room.lobby_password = lobby_password;
194
+                end
195
+            end
196
+        end
197
+    end);
198
+    host_module:hook("muc-disco#info", function (event)
199
+        if (event.room._data.lobbyroom) then
200
+            table.insert(event.form, {
201
+                name = "muc#roominfo_lobbyroom";
202
+                label = "Lobby room jid";
203
+                value = "";
204
+            });
205
+            event.formdata["muc#roominfo_lobbyroom"] = event.room._data.lobbyroom;
206
+        end
207
+    end);
208
+
209
+    host_module:hook('muc-occupant-pre-join', function (event)
210
+        local room, stanza = event.room, event.stanza;
211
+
212
+        if is_healthcheck_room(room) then
213
+            return;
214
+        end
215
+
216
+        local join = stanza:get_child("x", MUC_NS);
217
+        if not join then
218
+            return;
219
+        end
220
+
221
+        local password = join:get_child_text("lobbySharedPassword");
222
+        if password and event.room.lobby_password and password == room.lobby_password then
223
+            local invitee = event.stanza.attr.from;
224
+            local affiliation = room:get_affiliation(invitee);
225
+            if not affiliation or affiliation == 0 then
226
+                event.occupant.role = 'participant';
227
+                room:set_affiliation(true, jid_bare(invitee), "member");
228
+                room:save();
229
+            end
230
+
231
+        -- we want to add the custom lobbyroom field to fill in the lobby room jid
232
+        elseif room._data.members_only then
233
+            local invitee = event.stanza.attr.from;
234
+            local affiliation = room:get_affiliation(invitee);
235
+            if not affiliation or affiliation == 'none' then
236
+                local reply = st.error_reply(stanza, 'auth', 'registration-required'):up();
237
+                reply.tags[1].attr.code = '407';
238
+                reply:tag('x', {xmlns = MUC_NS}):up();
239
+                reply:tag('lobbyroom'):text(room._data.lobbyroom);
240
+                event.origin.send(reply:tag('x', {xmlns = MUC_NS}));
241
+                return true;
242
+            end
243
+        end
244
+    end, -4); -- the default hook on members_only module is on -5
245
+end);

+ 6
- 0
resources/prosody-plugins/mod_muc_meeting_id.lua View File

@@ -1,4 +1,5 @@
1 1
 local uuid_gen = require "util.uuid".generate;
2
+local is_healthcheck_room = module:require "util".is_healthcheck_room;
2 3
 
3 4
 -- Module that generates a unique meetingId, attaches it to the room
4 5
 -- and adds it to all disco info form data (when room is queried or in the
@@ -7,6 +8,11 @@ local uuid_gen = require "util.uuid".generate;
7 8
 -- Hook to assign meetingId for new rooms
8 9
 module:hook("muc-room-created", function(event)
9 10
     local room = event.room;
11
+
12
+    if is_healthcheck_room(room) then
13
+        return;
14
+    end
15
+
10 16
     room._data.meetingId = uuid_gen();
11 17
 
12 18
     module:log("debug", "Created meetingId:%s for %s",

+ 20
- 0
resources/prosody-plugins/mod_speakerstats_component.lua View File

@@ -1,5 +1,6 @@
1 1
 local get_room_from_jid = module:require "util".get_room_from_jid;
2 2
 local room_jid_match_rewrite = module:require "util".room_jid_match_rewrite;
3
+local is_healthcheck_room = module:require "util".is_healthcheck_room;
3 4
 local jid_resource = require "util.jid".resource;
4 5
 local ext_events = module:require "ext_events"
5 6
 local st = require "util.stanza";
@@ -111,12 +112,22 @@ end
111 112
 -- create speakerStats for the room
112 113
 function room_created(event)
113 114
     local room = event.room;
115
+
116
+    if is_healthcheck_room(room) then
117
+        return;
118
+    end
119
+
114 120
     room.speakerStats = {};
115 121
 end
116 122
 
117 123
 -- Create SpeakerStats object for the joined user
118 124
 function occupant_joined(event)
119 125
     local room = event.room;
126
+
127
+    if is_healthcheck_room(room) then
128
+        return;
129
+    end
130
+
120 131
     local occupant = event.occupant;
121 132
 
122 133
     local nick = jid_resource(occupant.nick);
@@ -172,6 +183,11 @@ end
172 183
 -- display name
173 184
 function occupant_leaving(event)
174 185
     local room = event.room;
186
+
187
+    if is_healthcheck_room(room) then
188
+        return;
189
+    end
190
+
175 191
     local occupant = event.occupant;
176 192
 
177 193
     local speakerStatsForOccupant = room.speakerStats[occupant.jid];
@@ -189,6 +205,10 @@ end
189 205
 function room_destroyed(event)
190 206
     local room = event.room;
191 207
 
208
+    if is_healthcheck_room(room) then
209
+        return;
210
+    end
211
+
192 212
     ext_events.speaker_stats(room, room.speakerStats);
193 213
 end
194 214
 

+ 14
- 1
resources/prosody-plugins/util.lib.lua View File

@@ -66,7 +66,7 @@ function async_handler_wrapper(event, handler)
66 66
     end
67 67
 
68 68
     local runner = async.runner;
69
-    
69
+
70 70
     -- Grab a local response so that we can send the http response when
71 71
     -- the handler is done.
72 72
     local response = event.response;
@@ -172,8 +172,21 @@ function is_feature_allowed(session, feature)
172 172
     end
173 173
 end
174 174
 
175
+function starts_with(str, start)
176
+    return str:sub(1, #start) == start
177
+end
178
+-- healthcheck rooms in jicofo starts with a string '__jicofo-health-check'
179
+function is_healthcheck_room(room)
180
+    if starts_with(room.jid, "__jicofo-health-check") then
181
+        return true;
182
+    end
183
+
184
+    return false;
185
+end
186
+
175 187
 return {
176 188
     is_feature_allowed = is_feature_allowed;
189
+    is_healthcheck_room = is_healthcheck_room;
177 190
     get_room_from_jid = get_room_from_jid;
178 191
     async_handler_wrapper = async_handler_wrapper;
179 192
     room_jid_match_rewrite = room_jid_match_rewrite;

Loading…
Cancel
Save