|
@@ -0,0 +1,240 @@
|
|
1
|
+local bare = require "util.jid".bare;
|
|
2
|
+local generate_uuid = require "util.uuid".generate;
|
|
3
|
+local jid = require "util.jid";
|
|
4
|
+local neturl = require "net.url";
|
|
5
|
+local parse = neturl.parseQuery;
|
|
6
|
+local st = require "util.stanza";
|
|
7
|
+local get_room_from_jid = module:require "util".get_room_from_jid;
|
|
8
|
+
|
|
9
|
+-- Options
|
|
10
|
+local poltergeist_component
|
|
11
|
+ = module:get_option_string("poltergeist_component", module.host);
|
|
12
|
+
|
|
13
|
+-- table to store all poltergeists we create
|
|
14
|
+local poltergeists = {};
|
|
15
|
+
|
|
16
|
+-- poltergaist management functions
|
|
17
|
+
|
|
18
|
+-- Returns the room if available, work and in multidomain mode
|
|
19
|
+-- @param room_name the name of the room
|
|
20
|
+-- @param group name of the group (optional)
|
|
21
|
+-- @return returns room if found or nil
|
|
22
|
+function get_room(room_name, group)
|
|
23
|
+ local room_address = jid.join(room_name, module:get_host());
|
|
24
|
+ -- if there is a group we are in multidomain mode
|
|
25
|
+ if group and group ~= "" then
|
|
26
|
+ room_address = "["..group.."]"..room_address;
|
|
27
|
+ end
|
|
28
|
+
|
|
29
|
+ return get_room_from_jid(room_address);
|
|
30
|
+end
|
|
31
|
+
|
|
32
|
+-- Stores the username in the table where we store poltergeist usernames
|
|
33
|
+-- based on their room names
|
|
34
|
+-- @param room the room instance
|
|
35
|
+-- @param user_id the user id
|
|
36
|
+-- @param username the username to store
|
|
37
|
+function store_username(room, user_id, username)
|
|
38
|
+ local room_name = jid.node(room.jid);
|
|
39
|
+
|
|
40
|
+ -- we store in poltergeist user ids for room names
|
|
41
|
+ if (not poltergeists[room_name]) then
|
|
42
|
+ poltergeists[room_name] = {};
|
|
43
|
+ end
|
|
44
|
+ poltergeists[room_name][user_id] = username;
|
|
45
|
+ log("debug", "stored in session: %s", username);
|
|
46
|
+end
|
|
47
|
+
|
|
48
|
+-- Retrieve the username for a user
|
|
49
|
+-- @param room the room instance
|
|
50
|
+-- @param user_id the user id
|
|
51
|
+-- @return returns the stored username for user or nil
|
|
52
|
+function get_username(room, user_id)
|
|
53
|
+ local room_name = jid.node(room.jid);
|
|
54
|
+
|
|
55
|
+ if (not poltergeists[room_name]) then
|
|
56
|
+ return nil;
|
|
57
|
+ end
|
|
58
|
+
|
|
59
|
+ return poltergeists[room_name][user_id];
|
|
60
|
+end
|
|
61
|
+
|
|
62
|
+-- if we found that a session for a user with id has a poltergiest already
|
|
63
|
+-- created, retrieve its jid and return it to the authentication
|
|
64
|
+-- so we can reuse it and we that real user will replace the poltergiest
|
|
65
|
+prosody.events.add_handler("pre-jitsi-authentication", function(session)
|
|
66
|
+
|
|
67
|
+ if (session.jitsi_meet_context_user) then
|
|
68
|
+ local room = get_room(
|
|
69
|
+ session.jitsi_bosh_query_room,
|
|
70
|
+ session.jitsi_meet_context_group);
|
|
71
|
+
|
|
72
|
+ if (not room) then
|
|
73
|
+ return nil;
|
|
74
|
+ end
|
|
75
|
+
|
|
76
|
+ local username
|
|
77
|
+ = get_username(room, session.jitsi_meet_context_user["id"]);
|
|
78
|
+
|
|
79
|
+ if (not username) then
|
|
80
|
+ return nil;
|
|
81
|
+ end
|
|
82
|
+
|
|
83
|
+ log("debug", "Found predefined username %s", username);
|
|
84
|
+
|
|
85
|
+ -- let's find the room and if the poltergeist occupant is there
|
|
86
|
+ -- lets remove him before the real participant joins
|
|
87
|
+ -- when we see the unavailable presence to go out the server
|
|
88
|
+ -- we will mark it with ignore tag
|
|
89
|
+ local nick = string.sub(username, 0, 8);
|
|
90
|
+ if (have_poltergeist_occupant(room, nick)) then
|
|
91
|
+ remove_poltergeist_occupant(room, nick);
|
|
92
|
+ end
|
|
93
|
+
|
|
94
|
+ return username;
|
|
95
|
+ end
|
|
96
|
+
|
|
97
|
+ return nil;
|
|
98
|
+end);
|
|
99
|
+
|
|
100
|
+-- Creates poltergeist occupant
|
|
101
|
+-- @param room the room instance where we create the occupant
|
|
102
|
+-- @param nick the nick to use for the new occupant
|
|
103
|
+-- @param name the display name fot the occupant (optional)
|
|
104
|
+-- @param avatar the avatar to use for the new occupant (optional)
|
|
105
|
+function create_poltergeist_occupant(room, nick, name, avatar)
|
|
106
|
+ log("debug", "create_poltergeist_occupant %s:", nick);
|
|
107
|
+ -- Join poltergeist occupant to room, with the invited JID as their nick
|
|
108
|
+ local join_presence = st.presence({
|
|
109
|
+ to = room.jid.."/"..nick,
|
|
110
|
+ from = poltergeist_component.."/"..nick
|
|
111
|
+ }):tag("x", { xmlns = "http://jabber.org/protocol/muc" }):up();
|
|
112
|
+
|
|
113
|
+ if (name) then
|
|
114
|
+ join_presence:tag(
|
|
115
|
+ "nick",
|
|
116
|
+ { xmlns = "http://jabber.org/protocol/nick" }):text(name):up();
|
|
117
|
+ end
|
|
118
|
+ if (avatar) then
|
|
119
|
+ join_presence:tag("avatar-url"):text(avatar):up();
|
|
120
|
+ end
|
|
121
|
+
|
|
122
|
+ room:handle_first_presence(
|
|
123
|
+ prosody.hosts[poltergeist_component], join_presence);
|
|
124
|
+end
|
|
125
|
+
|
|
126
|
+-- Removes poltergeist occupant
|
|
127
|
+-- @param room the room instance where to remove the occupant
|
|
128
|
+-- @param nick the nick of the occupant to remove
|
|
129
|
+function remove_poltergeist_occupant(room, nick)
|
|
130
|
+ log("debug", "remove_poltergeist_occupant %s", nick);
|
|
131
|
+ local leave_presence = st.presence({
|
|
132
|
+ to = room.jid.."/"..nick,
|
|
133
|
+ from = poltergeist_component.."/"..nick,
|
|
134
|
+ type = "unavailable" });
|
|
135
|
+ room:handle_normal_presence(
|
|
136
|
+ prosody.hosts[poltergeist_component], leave_presence);
|
|
137
|
+end
|
|
138
|
+
|
|
139
|
+-- Checks for existance of a poltergeist occupant
|
|
140
|
+-- @param room the room instance where to check for occupant
|
|
141
|
+-- @param nick the nick of the occupant
|
|
142
|
+-- @return true if occupant is found, false otherwise
|
|
143
|
+function have_poltergeist_occupant(room, nick)
|
|
144
|
+ -- Find out if we have a poltergeist occupant in the room for this JID
|
|
145
|
+ return not not room:get_occupant_jid(poltergeist_component.."/"..nick);
|
|
146
|
+end
|
|
147
|
+
|
|
148
|
+-- Event handlers
|
|
149
|
+
|
|
150
|
+--- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
|
|
151
|
+--- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
|
|
152
|
+module:hook("muc-decline", function (event)
|
|
153
|
+ remove_poltergeist_occupant(event.room, bare(event.stanza.attr.from));
|
|
154
|
+end, -100);
|
|
155
|
+-- before sending the presence for a poltergeist leaving add ignore tag
|
|
156
|
+-- as poltergeist is leaving just before the real user joins and in the client
|
|
157
|
+-- we ignore this presence to avoid leaving/joining experience and the real
|
|
158
|
+-- user will reuse all currently created UI components for the same nick
|
|
159
|
+module:hook("muc-broadcast-presence", function (event)
|
|
160
|
+ if (bare(event.occupant.jid) == poltergeist_component) then
|
|
161
|
+ if(event.stanza.attr.type == "unavailable") then
|
|
162
|
+ event.stanza:tag(
|
|
163
|
+ "ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
|
|
164
|
+ end
|
|
165
|
+ end
|
|
166
|
+end, -100);
|
|
167
|
+
|
|
168
|
+--- Handles request for creating/managing poltergeists
|
|
169
|
+-- @param event the http event, holds the request query
|
|
170
|
+-- @return GET response, containing a json with response details
|
|
171
|
+function handle_create_poltergeist (event)
|
|
172
|
+ local params = parse(event.request.url.query);
|
|
173
|
+ local user_id = params["user"];
|
|
174
|
+ local room_name = params["room"];
|
|
175
|
+ local group = params["group"];
|
|
176
|
+ local name = params["name"];
|
|
177
|
+ local avatar = params["avatar"];
|
|
178
|
+
|
|
179
|
+ local room = get_room(room_name, group);
|
|
180
|
+ if (not room) then
|
|
181
|
+ log("error", "no room found %s", room_address);
|
|
182
|
+ return 404;
|
|
183
|
+ end
|
|
184
|
+
|
|
185
|
+ local username = generate_uuid();
|
|
186
|
+ store_username(room, user_id, username)
|
|
187
|
+
|
|
188
|
+ create_poltergeist_occupant(room, string.sub(username,0,8), name, avatar);
|
|
189
|
+
|
|
190
|
+ return 200;
|
|
191
|
+end
|
|
192
|
+
|
|
193
|
+--- Handles request for updating poltergeists status
|
|
194
|
+-- @param event the http event, holds the request query
|
|
195
|
+-- @return GET response, containing a json with response details
|
|
196
|
+function handle_update_poltergeist (event)
|
|
197
|
+ local params = parse(event.request.url.query);
|
|
198
|
+ local user_id = params["user"];
|
|
199
|
+ local room_name = params["room"];
|
|
200
|
+ local group = params["group"];
|
|
201
|
+ local status = params["status"];
|
|
202
|
+
|
|
203
|
+ local room = get_room(room_name, group);
|
|
204
|
+ if (not room) then
|
|
205
|
+ log("error", "no room found %s", room_address);
|
|
206
|
+ return 404;
|
|
207
|
+ end
|
|
208
|
+
|
|
209
|
+ local username = get_username(room, user_id);
|
|
210
|
+ if (not username) then
|
|
211
|
+ return 404;
|
|
212
|
+ end
|
|
213
|
+
|
|
214
|
+ local nick = string.sub(username, 0, 8);
|
|
215
|
+ if (have_poltergeist_occupant(room, nick)) then
|
|
216
|
+ local update_presence = st.presence({
|
|
217
|
+ to = room.jid.."/"..nick,
|
|
218
|
+ from = poltergeist_component.."/"..nick
|
|
219
|
+ }):tag("status"):text(status):up();
|
|
220
|
+
|
|
221
|
+ room:handle_normal_presence(
|
|
222
|
+ prosody.hosts[poltergeist_component], update_presence);
|
|
223
|
+
|
|
224
|
+ return 200;
|
|
225
|
+ else
|
|
226
|
+ return 404;
|
|
227
|
+ end
|
|
228
|
+end
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+log("info", "Loading poltergeist service");
|
|
232
|
+module:depends("http");
|
|
233
|
+module:provides("http", {
|
|
234
|
+ default_path = "/";
|
|
235
|
+ name = "poltergeist";
|
|
236
|
+ route = {
|
|
237
|
+ ["GET /poltergeist/create"] = handle_create_poltergeist;
|
|
238
|
+ ["GET /poltergeist/update"] = handle_update_poltergeist;
|
|
239
|
+ };
|
|
240
|
+});
|