Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

mod_muc_lobby_rooms.lua 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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 "lobby.jitmeet.example.com" "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. if not have_async then
  21. module:log("warn", "Lobby rooms will not work with Prosody version 0.10 or less.");
  22. return;
  23. end
  24. local jid_split = require 'util.jid'.split;
  25. local jid_bare = require 'util.jid'.bare;
  26. local filters = require 'util.filters';
  27. local st = require 'util.stanza';
  28. local MUC_NS = 'http://jabber.org/protocol/muc';
  29. local DISCO_INFO_NS = 'http://jabber.org/protocol/disco#info';
  30. local DISPLAY_NAME_REQUIRED_FEATURE = 'http://jitsi.org/protocol/lobbyrooms#displayname_required';
  31. local LOBBY_IDENTITY_TYPE = 'lobbyrooms';
  32. local is_healthcheck_room = module:require "util".is_healthcheck_room;
  33. local main_muc_component_config = module:get_option_string('main_muc');
  34. if main_muc_component_config == nil then
  35. module:log('error', 'lobby not enabled missing main_muc config');
  36. return ;
  37. end
  38. local lobby_muc_component_config = module:get_option_string('lobby_muc');
  39. if lobby_muc_component_config == nil then
  40. module:log('error', 'lobby not enabled missing lobby_muc config');
  41. return ;
  42. end
  43. local whitelist;
  44. local check_display_name_required;
  45. local function load_config()
  46. whitelist = module:get_option_set("muc_lobby_whitelist", {});
  47. check_display_name_required
  48. = module:get_option_boolean("muc_lobby_check_display_name_required", true);
  49. end
  50. load_config();
  51. local lobby_muc_service;
  52. local main_muc_service;
  53. -- Checks whether there is self-status 110 of the <x node
  54. function check_self_status(muc_x)
  55. if not muc_x then
  56. return false;
  57. end
  58. for status in muc_x:childtags('status') do
  59. if status.attr.code == '110' then
  60. return true;
  61. end
  62. end
  63. return false;
  64. end
  65. function filter_stanza(stanza)
  66. if not stanza.attr or not stanza.attr.from or not main_muc_service then
  67. return stanza;
  68. end
  69. -- Allow self-presence (code=110)
  70. local node, from_domain = jid_split(stanza.attr.from);
  71. if from_domain == lobby_muc_component_config then
  72. if stanza.name == 'presence' then
  73. local muc_x = stanza:get_child('x', MUC_NS..'#user');
  74. if muc_x and check_self_status(muc_x) then
  75. return stanza;
  76. end
  77. -- check is an owner, only owners can receive the presence
  78. local room = main_muc_service.get_room_from_jid(jid_bare(node .. '@' .. main_muc_component_config));
  79. if room.get_affiliation(room, stanza.attr.to) == 'owner' then
  80. return stanza;
  81. end
  82. return nil;
  83. elseif stanza.name == 'iq' and stanza:get_child('query', DISCO_INFO_NS) then
  84. -- allow disco info from the lobby component
  85. return stanza;
  86. end
  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. -- process a host module directly if loaded or hooks to wait for its load
  99. function process_host_module(name, callback)
  100. local function process_host(host)
  101. if host == name then
  102. callback(module:context(host), host);
  103. end
  104. end
  105. if prosody.hosts[name] == nil then
  106. module:log('debug', 'No host/component found, will wait for it: %s', name)
  107. -- when a host or component is added
  108. prosody.events.add_handler('host-activated', process_host);
  109. else
  110. process_host(name);
  111. end
  112. end
  113. -- operates on already loaded lobby muc module
  114. function process_lobby_muc_loaded(lobby_muc, host_module)
  115. module:log('debug', 'Lobby muc loaded');
  116. lobby_muc_service = lobby_muc;
  117. -- enable filtering presences in the lobby muc rooms
  118. filters.add_filter_hook(filter_session);
  119. -- Advertise lobbyrooms support on main domain so client can pick up the address and use it
  120. module:add_identity('component', LOBBY_IDENTITY_TYPE, lobby_muc_component_config);
  121. -- Tag the disco#info response with a feature that display name is required
  122. -- when the conference name from the web request has a lobby enabled.
  123. host_module:hook("host-disco-info-node", function (event)
  124. local session, reply, node = event.origin, event.reply, event.node;
  125. if node == LOBBY_IDENTITY_TYPE
  126. and session.jitsi_web_query_room
  127. and main_muc_service
  128. and check_display_name_required then
  129. local room = main_muc_service.get_room_from_jid(
  130. jid_bare(session.jitsi_web_query_room .. '@' .. main_muc_component_config));
  131. if room and room._data.lobbyroom then
  132. reply:tag("feature", { var = DISPLAY_NAME_REQUIRED_FEATURE }):up();
  133. end
  134. end
  135. event.exists = true;
  136. end);
  137. local room_mt = lobby_muc_service.room_mt;
  138. -- we base affiliations (roles) in lobby muc component to be based on the roles in the main muc
  139. room_mt.get_affiliation = function(room, jid)
  140. if not room.main_room then
  141. module:log('error', 'No main room(%s) for %s!', room.jid, jid);
  142. return 'none';
  143. end
  144. -- moderators in main room are moderators here
  145. local role = room.main_room.get_affiliation(room.main_room, jid);
  146. if role then
  147. return role;
  148. end
  149. return 'none';
  150. end
  151. end
  152. -- process or waits to process the lobby muc component
  153. process_host_module(lobby_muc_component_config, function(host_module, host)
  154. -- lobby muc component created
  155. module:log('info', 'Lobby component loaded %s', host);
  156. local muc_module = prosody.hosts[host].modules.muc;
  157. if muc_module then
  158. process_lobby_muc_loaded(muc_module, host_module);
  159. else
  160. module:log('debug', 'Will wait for muc to be available');
  161. prosody.hosts[host].events.add_handler('module-loaded', function(event)
  162. if (event.module == 'muc') then
  163. process_lobby_muc_loaded(prosody.hosts[host].modules.muc, host_module);
  164. end
  165. end);
  166. end
  167. end);
  168. -- process or waits to process the main muc component
  169. process_host_module(main_muc_component_config, function(host_module, host)
  170. main_muc_service = prosody.hosts[host].modules.muc;
  171. -- hooks when lobby is enabled to create its room, only done here or by admin
  172. host_module:hook('muc-config-submitted', function(event)
  173. local room = event.room;
  174. local members_only = event.fields['muc#roomconfig_membersonly'] and true or nil;
  175. if members_only then
  176. local node = jid_split(room.jid);
  177. local lobby_room_jid = node .. '@' .. lobby_muc_component_config;
  178. if not lobby_muc_service.get_room_from_jid(lobby_room_jid) then
  179. local new_room = lobby_muc_service.create_room(lobby_room_jid);
  180. new_room.main_room = room;
  181. room._data.lobbyroom = new_room;
  182. event.status_codes["104"] = true;
  183. end
  184. elseif room._data.lobbyroom then
  185. room._data.lobbyroom:destroy(room.jid, 'Lobby room closed.');
  186. room._data.lobbyroom = nil;
  187. end
  188. end);
  189. host_module:hook("muc-room-destroyed",function(event)
  190. local room = event.room;
  191. if room._data.lobbyroom then
  192. room._data.lobbyroom:destroy(nil, 'Lobby room closed.');
  193. room._data.lobbyroom = nil;
  194. end
  195. end);
  196. host_module:hook("muc-disco#info", function (event)
  197. local room = event.room;
  198. if (room._data.lobbyroom and room:get_members_only()) then
  199. table.insert(event.form, {
  200. name = "muc#roominfo_lobbyroom";
  201. label = "Lobby room jid";
  202. value = "";
  203. });
  204. event.formdata["muc#roominfo_lobbyroom"] = room._data.lobbyroom.jid;
  205. end
  206. end);
  207. host_module:hook('muc-occupant-pre-join', function (event)
  208. local room, stanza = event.room, event.stanza;
  209. if is_healthcheck_room(room.jid) or not room:get_members_only() then
  210. return;
  211. end
  212. local join = stanza:get_child("x", MUC_NS);
  213. if not join then
  214. return;
  215. end
  216. local invitee = event.stanza.attr.from;
  217. local invitee_bare_jid = jid_bare(invitee);
  218. local _, invitee_domain = jid_split(invitee);
  219. local whitelistJoin = false;
  220. -- whitelist participants
  221. if whitelist:contains(invitee_domain) or whitelist:contains(invitee_bare_jid) then
  222. whitelistJoin = true;
  223. end
  224. local password = join:get_child_text('password', MUC_NS);
  225. if password and room:get_password() and password == room:get_password() then
  226. whitelistJoin = true;
  227. end
  228. if whitelistJoin then
  229. local affiliation = room:get_affiliation(invitee);
  230. if not affiliation or affiliation == 0 then
  231. event.occupant.role = 'participant';
  232. room:set_affiliation(true, invitee_bare_jid, "member");
  233. room:save();
  234. return;
  235. end
  236. end
  237. -- we want to add the custom lobbyroom field to fill in the lobby room jid
  238. local invitee = event.stanza.attr.from;
  239. local affiliation = room:get_affiliation(invitee);
  240. if not affiliation or affiliation == 'none' then
  241. local reply = st.error_reply(stanza, 'auth', 'registration-required'):up();
  242. reply.tags[1].attr.code = '407';
  243. reply:tag('x', {xmlns = MUC_NS}):up();
  244. reply:tag('lobbyroom'):text(room._data.lobbyroom.jid);
  245. event.origin.send(reply:tag('x', {xmlns = MUC_NS}));
  246. return true;
  247. end
  248. end, -4); -- the default hook on members_only module is on -5
  249. end);
  250. -- Extract 'room' param from URL when session is created
  251. function update_session(event)
  252. local session = event.session;
  253. if session.jitsi_web_query_room then
  254. -- no need for an update
  255. return;
  256. end
  257. local query = event.request.url.query;
  258. if query ~= nil then
  259. local params = formdecode(query);
  260. -- The room name and optional prefix from the web query
  261. session.jitsi_web_query_room = params.room;
  262. session.jitsi_web_query_prefix = params.prefix or "";
  263. end
  264. end
  265. module:hook_global("bosh-session", update_session);
  266. module:hook_global("websocket-session", update_session);
  267. module:hook_global('config-reloaded', load_config);