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

mod_visitors_component.lua 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. module:log('info', 'Starting visitors_component at %s', module.host);
  2. local jid = require 'util.jid';
  3. local st = require 'util.stanza';
  4. local util = module:require 'util';
  5. local room_jid_match_rewrite = util.room_jid_match_rewrite;
  6. local get_room_from_jid = util.get_room_from_jid;
  7. local get_focus_occupant = util.get_focus_occupant;
  8. local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
  9. local new_id = require 'util.id'.medium;
  10. local um_is_admin = require 'core.usermanager'.is_admin;
  11. local MUC_NS = 'http://jabber.org/protocol/muc';
  12. local muc_domain_prefix = module:get_option_string('muc_mapper_domain_prefix', 'conference');
  13. local muc_domain_base = module:get_option_string('muc_mapper_domain_base');
  14. if not muc_domain_base then
  15. module:log('warn', 'No muc_domain_base option set.');
  16. return;
  17. end
  18. local auto_allow_promotion = module:get_option_boolean('auto_allow_visitor_promotion', false);
  19. local function is_admin(jid)
  20. return um_is_admin(jid, module.host);
  21. end
  22. -- This is a map to keep data for room and the jids that were allowed to join after visitor mode is enabled
  23. -- automatically allowed or allowed by a moderator
  24. local visitors_promotion_map = {};
  25. local sent_iq_cache = require 'util.cache'.new(200);
  26. -- send iq result that the iq was received and will be processed
  27. local function respond_iq_result(origin, stanza)
  28. -- respond with successful receiving the iq
  29. origin.send(st.iq({
  30. type = 'result';
  31. from = stanza.attr.to;
  32. to = stanza.attr.from;
  33. id = stanza.attr.id
  34. }));
  35. end
  36. local function request_promotion_received(room, from_jid, from_vnode)
  37. if not visitors_promotion_map[room.jid] and auto_allow_promotion then
  38. -- visitors is enabled
  39. visitors_promotion_map[room.jid] = {};
  40. end
  41. -- if visitors is enabled for the room
  42. if visitors_promotion_map[room.jid] then
  43. if auto_allow_promotion then
  44. -- we are in auto-allow mode, let's reply with accept
  45. -- we store where the request is coming from so we can send back the response
  46. local username = new_id():lower();
  47. visitors_promotion_map[room.jid][username] = {
  48. from = from_vnode;
  49. jid = from_jid;
  50. };
  51. local req_from = visitors_promotion_map[room.jid][username].from;
  52. local req_jid = visitors_promotion_map[room.jid][username].jid;
  53. local focus_occupant = get_focus_occupant(room);
  54. local focus_jid = focus_occupant and focus_occupant.bare_jid or nil;
  55. local iq_id = new_id();
  56. sent_iq_cache:set(iq_id, socket.gettime());
  57. module:send(st.iq({
  58. type='set', to = req_from, from = module.host, id = iq_id })
  59. :tag('visitors', {
  60. xmlns='jitsi:visitors',
  61. room = string.gsub(room.jid, muc_domain_base, req_from),
  62. focusjid = focus_jid })
  63. :tag('promotion-response', {
  64. xmlns='jitsi:visitors',
  65. jid = req_jid,
  66. username = username ,
  67. allow = 'true' }):up());
  68. return true;
  69. end
  70. -- TODO send promotion request to all moderators
  71. module:log('warn', 'Received promotion request from %s for room %s without active visitors', from, room.jid);
  72. return;
  73. end
  74. end
  75. local function connect_vnode_received(room, vnode)
  76. module:context(muc_domain_base):fire_event('jitsi-connect-vnode', { room = room; vnode = vnode; });
  77. end
  78. local function disconnect_vnode_received(room, vnode)
  79. module:context(muc_domain_base):fire_event('jitsi-disconnect-vnode', { room = room; vnode = vnode; });
  80. end
  81. -- listens for iq request for promotion and forward it to moderators in the meeting for approval
  82. -- or auto-allow it if such the config is set enabling it
  83. local function stanza_handler(event)
  84. local origin, stanza = event.origin, event.stanza;
  85. if stanza.name ~= 'iq' then
  86. return;
  87. end
  88. if stanza.attr.type == 'result' and sent_iq_cache:get(stanza.attr.id) then
  89. sent_iq_cache:set(stanza.attr.id, nil);
  90. return true;
  91. end
  92. if stanza.attr.type ~= 'set' and stanza.attr.type ~= 'get' then
  93. return; -- We do not want to reply to these, so leave.
  94. end
  95. local visitors_iq = event.stanza:get_child('visitors', 'jitsi:visitors');
  96. if not visitors_iq then
  97. return;
  98. end
  99. -- set stanzas are coming from s2s connection
  100. if stanza.attr.type == 'set' and origin.type ~= 's2sin' then
  101. module:log('warn', 'not from s2s session, ignore! %s', stanza);
  102. return true;
  103. end
  104. local room_jid = visitors_iq.attr.room;
  105. local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
  106. if not room then
  107. module:log('debug', 'No room found %s', room_jid);
  108. return;
  109. end
  110. local processed;
  111. -- promotion request is coming from visitors and is a set and is over the s2s connection
  112. local request_promotion = visitors_iq:get_child('promotion-request');
  113. if request_promotion then
  114. processed = request_promotion_received(room, request_promotion.attr.jid, stanza.attr.from);
  115. end
  116. -- connect and disconnect are only received from jicofo
  117. if is_admin(jid.bare(stanza.attr.from)) then
  118. for item in visitors_iq:childtags('connect-vnode') do
  119. connect_vnode_received(room, item.attr.vnode);
  120. processed = true;
  121. end
  122. for item in visitors_iq:childtags('disconnect-vnode') do
  123. disconnect_vnode_received(room, item.attr.vnode);
  124. processed = true;
  125. end
  126. end
  127. if not processed then
  128. module:log('warn', 'Unknown iq received for %s: %s', module.host, stanza);
  129. end
  130. respond_iq_result(origin, stanza);
  131. return processed;
  132. end
  133. module:hook('iq/host', stanza_handler, 10);
  134. --process a host module directly if loaded or hooks to wait for its load
  135. function process_host_module(name, callback)
  136. local function process_host(host)
  137. if host == name then
  138. callback(module:context(host), host);
  139. end
  140. end
  141. if prosody.hosts[name] == nil then
  142. module:log('debug', 'No host/component found, will wait for it: %s', name)
  143. -- when a host or component is added
  144. prosody.events.add_handler('host-activated', process_host);
  145. else
  146. process_host(name);
  147. end
  148. end
  149. process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_module, host)
  150. -- if visitor mode is started, then you are not allowed to join without request/response exchange of iqs -> deny access
  151. -- check list of allowed jids for the room
  152. host_module:hook('muc-occupant-pre-join', function (event)
  153. local room, stanza, origin = event.room, event.stanza, event.origin;
  154. -- visitors were already in the room one way or another they have access
  155. -- skip password challenge
  156. local join = stanza:get_child('x', MUC_NS);
  157. if join and room:get_password() then
  158. join:tag('password', { xmlns = MUC_NS }):text(room:get_password());
  159. end
  160. -- we skip any checks when auto-allow is enabled
  161. if auto_allow_promotion then
  162. return;
  163. end
  164. if visitors_promotion_map[room.jid] then
  165. -- now let's check for jid
  166. if visitors_promotion_map[room.jid] and visitors_promotion_map[room.jid][jid.node(stanza.attr.from)] then
  167. -- allow join
  168. return;
  169. end
  170. origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitor needs to be allowed by a moderator'));
  171. return true;
  172. end
  173. end, 7); -- after muc_meeting_id, the logic for not joining before jicofo
  174. host_module:hook('muc-room-destroyed', function (event)
  175. visitors_promotion_map[event.room.jid] = nil;
  176. end);
  177. end);
  178. -- enable only in case of auto-allow is enabled
  179. if auto_allow_promotion then
  180. prosody.events.add_handler('pre-jitsi-authentication', function(session)
  181. if not session.customusername or not session.jitsi_web_query_room then
  182. return nil;
  183. end
  184. local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
  185. if not room then
  186. return nil;
  187. end
  188. if visitors_promotion_map[room.jid] and visitors_promotion_map[room.jid][session.customusername] then
  189. -- user was previously allowed to join, let him use the requested jid
  190. return session.customusername;
  191. end
  192. end);
  193. end