您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

mod_muc_poltergeist.lua 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. local bare = require "util.jid".bare;
  2. local get_room_from_jid = module:require "util".get_room_from_jid;
  3. local jid = require "util.jid";
  4. local neturl = require "net.url";
  5. local parse = neturl.parseQuery;
  6. local poltergeist = module:require "poltergeist";
  7. local have_async = pcall(require, "util.async");
  8. if not have_async then
  9. module:log("error", "requires a version of Prosody with util.async");
  10. return;
  11. end
  12. local wrap_async_run = module:require "util".wrap_async_run;
  13. -- Options
  14. local poltergeist_component
  15. = module:get_option_string("poltergeist_component", module.host);
  16. -- this basically strips the domain from the conference.domain address
  17. local parentHostName = string.gmatch(tostring(module.host), "%w+.(%w.+)")();
  18. if parentHostName == nil then
  19. log("error", "Failed to start - unable to get parent hostname");
  20. return;
  21. end
  22. local parentCtx = module:context(parentHostName);
  23. if parentCtx == nil then
  24. log("error",
  25. "Failed to start - unable to get parent context for host: %s",
  26. tostring(parentHostName));
  27. return;
  28. end
  29. local token_util = module:require "token/util".new(parentCtx);
  30. -- option to enable/disable token verifications
  31. local disableTokenVerification
  32. = module:get_option_boolean("disable_polergeist_token_verification", false);
  33. -- poltergaist management functions
  34. -- Returns the room if available, work and in multidomain mode
  35. -- @param room_name the name of the room
  36. -- @param group name of the group (optional)
  37. -- @return returns room if found or nil
  38. function get_room(room_name, group)
  39. local room_address = jid.join(room_name, module:get_host());
  40. -- if there is a group we are in multidomain mode and that group is not
  41. -- our parent host
  42. if group and group ~= "" and group ~= parentHostName then
  43. room_address = "["..group.."]"..room_address;
  44. end
  45. return get_room_from_jid(room_address);
  46. end
  47. --- Verifies room name, domain name with the values in the token
  48. -- @param token the token we received
  49. -- @param room_name the room name
  50. -- @param group name of the group (optional)
  51. -- @param session the session to use for storing token specific fields
  52. -- @return true if values are ok or false otherwise
  53. function verify_token(token, room_name, group, session)
  54. if disableTokenVerification then
  55. return true;
  56. end
  57. -- if not disableTokenVerification and we do not have token
  58. -- stop here, cause the main virtual host can have guest access enabled
  59. -- (allowEmptyToken = true) and we will allow access to rooms info without
  60. -- a token
  61. if token == nil then
  62. log("warn", "no token provided");
  63. return false;
  64. end
  65. session.auth_token = token;
  66. local verified, reason = token_util:process_and_verify_token(session);
  67. if not verified then
  68. log("warn", "not a valid token %s", tostring(reason));
  69. return false;
  70. end
  71. local room_address = jid.join(room_name, module:get_host());
  72. -- if there is a group we are in multidomain mode and that group is not
  73. -- our parent host
  74. if group and group ~= "" and group ~= parentHostName then
  75. room_address = "["..group.."]"..room_address;
  76. end
  77. if not token_util:verify_room(session, room_address) then
  78. log("warn", "Token %s not allowed to join: %s",
  79. tostring(token), tostring(room_address));
  80. return false;
  81. end
  82. return true;
  83. end
  84. -- Event handlers
  85. -- if we found that a session for a user with id has a poltergiest already
  86. -- created, retrieve its jid and return it to the authentication
  87. -- so we can reuse it and we that real user will replace the poltergiest
  88. prosody.events.add_handler("pre-jitsi-authentication", function(session)
  89. if (session.jitsi_meet_context_user) then
  90. local room = get_room(
  91. session.jitsi_bosh_query_room,
  92. session.jitsi_bosh_query_prefix);
  93. if (not room) then
  94. return nil;
  95. end
  96. local username = poltergeist.get_username(
  97. room,
  98. session.jitsi_meet_context_user["id"]
  99. );
  100. if (not username) then
  101. return nil;
  102. end
  103. log("debug", "Found predefined username %s", username);
  104. -- let's find the room and if the poltergeist occupant is there
  105. -- lets remove him before the real participant joins
  106. -- when we see the unavailable presence to go out the server
  107. -- we will mark it with ignore tag
  108. local nick = poltergeist.create_nick(username);
  109. if (poltergeist.occupies(room, nick)) then
  110. module:log("info", "swapping poltergeist for user: %s/%s", room, nick)
  111. -- notify that user connected using the poltergeist
  112. poltergeist.update(room, nick, "connected");
  113. poltergeist.remove(room, nick, true);
  114. end
  115. return username;
  116. end
  117. return nil;
  118. end);
  119. --- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
  120. --- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
  121. module:hook("muc-decline", function (event)
  122. poltergeist.remove(event.room, bare(event.stanza.attr.from), false);
  123. end, -100);
  124. -- before sending the presence for a poltergeist leaving add ignore tag
  125. -- as poltergeist is leaving just before the real user joins and in the client
  126. -- we ignore this presence to avoid leaving/joining experience and the real
  127. -- user will reuse all currently created UI components for the same nick
  128. module:hook("muc-broadcast-presence", function (event)
  129. if (bare(event.occupant.jid) == poltergeist_component) then
  130. if(event.stanza.attr.type == "unavailable"
  131. and poltergeist.should_ignore(event.occupant.nick)) then
  132. event.stanza:tag(
  133. "ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
  134. poltergeist.reset_ignored(event.occupant.nick);
  135. end
  136. end
  137. end, -100);
  138. -- cleanup room table after room is destroyed
  139. module:hook(
  140. "muc-room-destroyed",
  141. function(event)
  142. poltergeist.remove_room(event.room);
  143. end
  144. );
  145. --- Handles request for creating/managing poltergeists
  146. -- @param event the http event, holds the request query
  147. -- @return GET response, containing a json with response details
  148. function handle_create_poltergeist (event)
  149. if (not event.request.url.query) then
  150. return 400;
  151. end
  152. local params = parse(event.request.url.query);
  153. local user_id = params["user"];
  154. local room_name = params["room"];
  155. local group = params["group"];
  156. local name = params["name"];
  157. local avatar = params["avatar"];
  158. local status = params["status"];
  159. local conversation = params["conversation"];
  160. local session = {};
  161. if not verify_token(params["token"], room_name, group, session) then
  162. return 403;
  163. end
  164. -- If the provided room conference doesn't exist then we
  165. -- can't add a poltergeist to it.
  166. local room = get_room(room_name, group);
  167. if (not room) then
  168. log("error", "no room found %s", room_name);
  169. return 404;
  170. end
  171. -- If the poltergiest is already in the conference then it will
  172. -- be in our username store and another can't be added.
  173. local username = poltergeist.get_username(room, user_id);
  174. if (username ~=nil and
  175. poltergeist.occupies(room, poltergeist.create_nick(username))) then
  176. log("warn",
  177. "poltergeist for username:%s already in the room:%s",
  178. username,
  179. room_name
  180. );
  181. return 202;
  182. end
  183. local context = {
  184. user = {
  185. id = user_id;
  186. };
  187. group = group;
  188. creator_user = session.jitsi_meet_context_user;
  189. creator_group = session.jitsi_meet_context_group;
  190. };
  191. local resources = {};
  192. if conversation ~= nil then
  193. resources["conversation"] = conversation
  194. end
  195. poltergeist.add_to_muc(room, user_id, name, avatar, context, status, resources)
  196. return 200;
  197. end
  198. --- Handles request for updating poltergeists status
  199. -- @param event the http event, holds the request query
  200. -- @return GET response, containing a json with response details
  201. function handle_update_poltergeist (event)
  202. if (not event.request.url.query) then
  203. return 400;
  204. end
  205. local params = parse(event.request.url.query);
  206. local user_id = params["user"];
  207. local room_name = params["room"];
  208. local group = params["group"];
  209. local status = params["status"];
  210. local call_id = params["callid"];
  211. local call_cancel = false
  212. if params["callcancel"] == "true" then
  213. call_cancel = true;
  214. end
  215. if not verify_token(params["token"], room_name, group, {}) then
  216. return 403;
  217. end
  218. local room = get_room(room_name, group);
  219. if (not room) then
  220. log("error", "no room found %s", room_name);
  221. return 404;
  222. end
  223. local username = poltergeist.get_username(room, user_id);
  224. if (not username) then
  225. return 404;
  226. end
  227. local call_details = {
  228. ["cancel"] = call_cancel;
  229. ["id"] = call_id;
  230. };
  231. local nick = poltergeist.create_nick(username);
  232. if (not poltergeist.occupies(room, nick)) then
  233. return 404;
  234. end
  235. poltergeist.update(room, nick, status, call_details);
  236. return 200;
  237. end
  238. --- Handles remove poltergeists
  239. -- @param event the http event, holds the request query
  240. -- @return GET response, containing a json with response details
  241. function handle_remove_poltergeist (event)
  242. if (not event.request.url.query) then
  243. return 400;
  244. end
  245. local params = parse(event.request.url.query);
  246. local user_id = params["user"];
  247. local room_name = params["room"];
  248. local group = params["group"];
  249. if not verify_token(params["token"], room_name, group, {}) then
  250. return 403;
  251. end
  252. local room = get_room(room_name, group);
  253. if (not room) then
  254. log("error", "no room found %s", room_name);
  255. return 404;
  256. end
  257. local username = poltergeist.get_username(room, user_id);
  258. if (not username) then
  259. return 404;
  260. end
  261. local nick = poltergeist.create_nick(username);
  262. if (not poltergeist.occupies(room, nick)) then
  263. return 404;
  264. end
  265. poltergeist.remove(room, nick, false);
  266. return 200;
  267. end
  268. log("info", "Loading poltergeist service");
  269. module:depends("http");
  270. module:provides("http", {
  271. default_path = "/";
  272. name = "poltergeist";
  273. route = {
  274. ["GET /poltergeist/create"] = function (event) return wrap_async_run(event,handle_create_poltergeist) end;
  275. ["GET /poltergeist/update"] = function (event) return wrap_async_run(event,handle_update_poltergeist) end;
  276. ["GET /poltergeist/remove"] = function (event) return wrap_async_run(event,handle_remove_poltergeist) end;
  277. };
  278. });