Comparison

plugins/muc/muc.lib.lua @ 10434:8f709577fe8e

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Sat, 23 Nov 2019 23:12:01 +0100
parent 10431:3db8372e203c
parent 10361:6e051bfca12d
child 10449:2e36a54906e4
comparison
equal deleted inserted replaced
10433:7777f25d5266 10434:8f709577fe8e
21 local jid_resource = require "util.jid".resource; 21 local jid_resource = require "util.jid".resource;
22 local resourceprep = require "util.encodings".stringprep.resourceprep; 22 local resourceprep = require "util.encodings".stringprep.resourceprep;
23 local st = require "util.stanza"; 23 local st = require "util.stanza";
24 local base64 = require "util.encodings".base64; 24 local base64 = require "util.encodings".base64;
25 local md5 = require "util.hashes".md5; 25 local md5 = require "util.hashes".md5;
26 local new_id = require "util.id".medium;
26 27
27 local log = module._log; 28 local log = module._log;
28 29
29 local occupant_lib = module:require "muc/occupant" 30 local occupant_lib = module:require "muc/occupant"
30 local muc_util = module:require "muc/util"; 31 local muc_util = module:require "muc/util";
37 function room_mt:__tostring() 38 function room_mt:__tostring()
38 return "MUC room ("..self.jid..")"; 39 return "MUC room ("..self.jid..")";
39 end 40 end
40 41
41 function room_mt.save() 42 function room_mt.save()
42 -- overriden by mod_muc.lua 43 -- overridden by mod_muc.lua
43 end 44 end
44 45
45 function room_mt:get_occupant_jid(real_jid) 46 function room_mt:get_occupant_jid(real_jid)
46 return self._jid_nick[real_jid] 47 return self._jid_nick[real_jid]
47 end 48 end
215 end 216 end
216 end 217 end
217 218
218 -- Broadcasts an occupant's presence to the whole room 219 -- Broadcasts an occupant's presence to the whole room
219 -- Takes the x element that goes into the stanzas 220 -- Takes the x element that goes into the stanzas
220 function room_mt:publicise_occupant_status(occupant, x, nick, actor, reason) 221 function room_mt:publicise_occupant_status(occupant, x, nick, actor, reason, prev_role, force_unavailable)
221 local base_x = x.base or x; 222 local base_x = x.base or x;
222 -- Build real jid and (optionally) occupant jid template presences 223 -- Build real jid and (optionally) occupant jid template presences
223 local base_presence do 224 local base_presence do
224 -- Try to use main jid's presence 225 -- Try to use main jid's presence
225 local pr = occupant:get_presence(); 226 local pr = occupant:get_presence();
226 if pr and (occupant.role ~= nil or pr.attr.type == "unavailable") then 227 if pr and (occupant.role ~= nil or pr.attr.type == "unavailable") and not force_unavailable then
227 base_presence = st.clone(pr); 228 base_presence = st.clone(pr);
228 else -- user is leaving but didn't send a leave presence. make one for them 229 else -- user is leaving but didn't send a leave presence. make one for them
229 base_presence = st.presence {from = occupant.nick; type = "unavailable";}; 230 base_presence = st.presence {from = occupant.nick; type = "unavailable";};
230 end 231 end
231 end 232 end
277 self_x = st.clone(x.self or base_x); 278 self_x = st.clone(x.self or base_x);
278 self:build_item_list(occupant, self_x, false, nick, actor_nick, nil, reason); 279 self:build_item_list(occupant, self_x, false, nick, actor_nick, nil, reason);
279 self_p = st.clone(base_presence):add_child(self_x); 280 self_p = st.clone(base_presence):add_child(self_x);
280 end 281 end
281 282
282 -- General populance 283 local broadcast_roles = self:get_presence_broadcast();
284
285 -- General populace
283 for occupant_nick, n_occupant in self:each_occupant() do 286 for occupant_nick, n_occupant in self:each_occupant() do
284 if occupant_nick ~= occupant.nick then 287 if occupant_nick ~= occupant.nick then
285 local pr; 288 local pr;
286 if can_see_real_jids(whois, n_occupant) then 289 if can_see_real_jids(whois, n_occupant) then
287 pr = get_full_p(); 290 pr = get_full_p();
288 elseif occupant.bare_jid == n_occupant.bare_jid then 291 elseif occupant.bare_jid == n_occupant.bare_jid then
289 pr = self_p; 292 pr = self_p;
290 else 293 else
291 pr = get_anon_p(); 294 pr = get_anon_p();
292 end 295 end
293 self:route_to_occupant(n_occupant, pr); 296 if broadcast_roles[occupant.role or "none"] or force_unavailable then
297 self:route_to_occupant(n_occupant, pr);
298 elseif prev_role and broadcast_roles[prev_role] then
299 pr.attr.type = 'unavailable';
300 self:route_to_occupant(n_occupant, pr);
301 end
302
294 end 303 end
295 end 304 end
296 305
297 -- Presences for occupant itself 306 -- Presences for occupant itself
298 self_x:tag("status", {code = "110";}):up(); 307 self_x:tag("status", {code = "110";}):up();
312 321
313 function room_mt:send_occupant_list(to, filter) 322 function room_mt:send_occupant_list(to, filter)
314 local to_bare = jid_bare(to); 323 local to_bare = jid_bare(to);
315 local is_anonymous = false; 324 local is_anonymous = false;
316 local whois = self:get_whois(); 325 local whois = self:get_whois();
326 local broadcast_roles = self:get_presence_broadcast();
317 if whois ~= "anyone" then 327 if whois ~= "anyone" then
318 local affiliation = self:get_affiliation(to); 328 local affiliation = self:get_affiliation(to);
319 if affiliation ~= "admin" and affiliation ~= "owner" then 329 if affiliation ~= "admin" and affiliation ~= "owner" then
320 local occupant = self:get_occupant_by_real_jid(to); 330 local occupant = self:get_occupant_by_real_jid(to);
321 if not (occupant and can_see_real_jids(whois, occupant)) then 331 if not (occupant and can_see_real_jids(whois, occupant)) then
328 local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); 338 local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'});
329 self:build_item_list(occupant, x, is_anonymous and to_bare ~= occupant.bare_jid); -- can always see your own jids 339 self:build_item_list(occupant, x, is_anonymous and to_bare ~= occupant.bare_jid); -- can always see your own jids
330 local pres = st.clone(occupant:get_presence()); 340 local pres = st.clone(occupant:get_presence());
331 pres.attr.to = to; 341 pres.attr.to = to;
332 pres:add_child(x); 342 pres:add_child(x);
333 self:route_stanza(pres); 343 if to_bare == occupant.bare_jid or broadcast_roles[occupant.role or "none"] then
344 self:route_stanza(pres);
345 end
334 end 346 end
335 end 347 end
336 end 348 end
337 349
338 function room_mt:get_disco_info(stanza) 350 function room_mt:get_disco_info(stanza)
389 if is_last_session then 401 if is_last_session then
390 x:tag("status", {code = "333"}); 402 x:tag("status", {code = "333"});
391 end 403 end
392 self:publicise_occupant_status(new_occupant or occupant, x); 404 self:publicise_occupant_status(new_occupant or occupant, x);
393 if is_last_session then 405 if is_last_session then
394 module:fire_event("muc-occupant-left", {room = self; nick = occupant.nick; occupant = occupant;}); 406 module:fire_event("muc-occupant-left", {
407 room = self;
408 nick = occupant.nick;
409 occupant = occupant;
410 });
395 end 411 end
396 return true; 412 return true;
397 end 413 end
398 414
399 -- Give the room creator owner affiliation 415 -- Give the room creator owner affiliation
426 event.origin.send(st.error_reply(event.stanza, "modify", "not-allowed", "Invisible Nicknames are forbidden")); 442 event.origin.send(st.error_reply(event.stanza, "modify", "not-allowed", "Invisible Nicknames are forbidden"));
427 return true; 443 return true;
428 end 444 end
429 end, 1); 445 end, 1);
430 446
447 module:hook("muc-occupant-pre-join", function(event)
448 local nick = jid_resource(event.occupant.nick);
449 if not resourceprep(nick, true) then -- strict
450 event.origin.send(st.error_reply(event.stanza, "modify", "jid-malformed", "Nickname must pass strict validation"));
451 return true;
452 end
453 end, 2);
454
455 module:hook("muc-occupant-pre-change", function(event)
456 local nick = jid_resource(event.dest_occupant.nick);
457 if not resourceprep(nick, true) then -- strict
458 event.origin.send(st.error_reply(event.stanza, "modify", "jid-malformed", "Nickname must pass strict validation"));
459 return true;
460 end
461 end, 2);
462
431 function room_mt:handle_first_presence(origin, stanza) 463 function room_mt:handle_first_presence(origin, stanza)
432 if not stanza:get_child("x", "http://jabber.org/protocol/muc") then
433 module:log("debug", "Room creation without <x>, possibly desynced");
434
435 origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
436 return true;
437 end
438
439 local real_jid = stanza.attr.from; 464 local real_jid = stanza.attr.from;
440 local dest_jid = stanza.attr.to; 465 local dest_jid = stanza.attr.to;
441 local bare_jid = jid_bare(real_jid); 466 local bare_jid = jid_bare(real_jid);
442 if module:fire_event("muc-room-pre-create", { 467 if module:fire_event("muc-room-pre-create", {
443 room = self; 468 room = self;
503 local muc_x = stanza:get_child("x", "http://jabber.org/protocol/muc"); 528 local muc_x = stanza:get_child("x", "http://jabber.org/protocol/muc");
504 529
505 if orig_occupant == nil and not muc_x and stanza.attr.type == nil then 530 if orig_occupant == nil and not muc_x and stanza.attr.type == nil then
506 module:log("debug", "Attempted join without <x>, possibly desynced"); 531 module:log("debug", "Attempted join without <x>, possibly desynced");
507 origin.send(st.error_reply(stanza, "cancel", "item-not-found", 532 origin.send(st.error_reply(stanza, "cancel", "item-not-found",
508 "You must join the room before sending presence updates")); 533 "You are not currently connected to this chat"));
509 return true; 534 return true;
510 end 535 end
511 536
512 local is_first_dest_session; 537 local is_first_dest_session;
513 local dest_occupant; 538 local dest_occupant;
611 -- self:build_item_list(orig_occupant, x, false, dest_nick); 636 -- self:build_item_list(orig_occupant, x, false, dest_nick);
612 add_item(x, self:get_affiliation(bare_jid), orig_occupant.role, real_jid, dest_nick); 637 add_item(x, self:get_affiliation(bare_jid), orig_occupant.role, real_jid, dest_nick);
613 x:tag("status", {code = "303";}):up(); 638 x:tag("status", {code = "303";}):up();
614 x:tag("status", {code = "110";}):up(); 639 x:tag("status", {code = "110";}):up();
615 self:route_stanza(generated_unavail:add_child(x)); 640 self:route_stanza(generated_unavail:add_child(x));
616 dest_nick = nil; -- set dest_nick to nil; so general populance doesn't see it for whole orig_occupant 641 dest_nick = nil; -- set dest_nick to nil; so general populace doesn't see it for whole orig_occupant
617 end 642 end
618 end 643 end
619 644
620 self:save_occupant(orig_occupant); 645 self:save_occupant(orig_occupant);
621 self:publicise_occupant_status(orig_occupant, orig_x, dest_nick); 646 self:publicise_occupant_status(orig_occupant, orig_x, dest_nick);
877 self:save_occupant(occupant); 902 self:save_occupant(occupant);
878 occupants_updated[occupant] = true; 903 occupants_updated[occupant] = true;
879 end 904 end
880 for occupant in pairs(occupants_updated) do 905 for occupant in pairs(occupants_updated) do
881 self:publicise_occupant_status(occupant, x); 906 self:publicise_occupant_status(occupant, x);
882 module:fire_event("muc-occupant-left", { room = self; nick = occupant.nick; occupant = occupant;}); 907 module:fire_event("muc-occupant-left", {
908 room = self;
909 nick = occupant.nick;
910 occupant = occupant;
911 });
883 end 912 end
884 end 913 end
885 914
886 function room_mt:destroy(newjid, reason, password) 915 function room_mt:destroy(newjid, reason, password)
887 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) 916 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"})
970 local item = stanza.tags[1].tags[1]; 999 local item = stanza.tags[1].tags[1];
971 local _aff = item.attr.affiliation; 1000 local _aff = item.attr.affiliation;
972 local _aff_rank = valid_affiliations[_aff or "none"]; 1001 local _aff_rank = valid_affiliations[_aff or "none"];
973 local _rol = item.attr.role; 1002 local _rol = item.attr.role;
974 if _aff and _aff_rank and not _rol then 1003 if _aff and _aff_rank and not _rol then
975 -- You need to be at least an admin, and be requesting info about your affifiliation or lower 1004 -- You need to be at least an admin, and be requesting info about your affiliation or lower
976 -- e.g. an admin can't ask for a list of owners 1005 -- e.g. an admin can't ask for a list of owners
977 local affiliation_rank = valid_affiliations[affiliation or "none"]; 1006 local affiliation_rank = valid_affiliations[affiliation or "none"];
978 if (affiliation_rank >= valid_affiliations.admin and affiliation_rank >= _aff_rank) 1007 if (affiliation_rank >= valid_affiliations.admin and affiliation_rank >= _aff_rank)
979 or (self:get_whois() == "anyone") then 1008 or (self:get_whois() == "anyone") then
980 local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); 1009 local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin");
1047 end 1076 end
1048 1077
1049 function room_mt:handle_groupchat_to_room(origin, stanza) 1078 function room_mt:handle_groupchat_to_room(origin, stanza)
1050 local from = stanza.attr.from; 1079 local from = stanza.attr.from;
1051 local occupant = self:get_occupant_by_real_jid(from); 1080 local occupant = self:get_occupant_by_real_jid(from);
1081 if not stanza.attr.id then
1082 stanza.attr.id = new_id()
1083 end
1052 if module:fire_event("muc-occupant-groupchat", { 1084 if module:fire_event("muc-occupant-groupchat", {
1053 room = self; origin = origin; stanza = stanza; from = from; occupant = occupant; 1085 room = self; origin = origin; stanza = stanza; from = from; occupant = occupant;
1054 }) then return true; end 1086 }) then return true; end
1055 stanza.attr.from = occupant.nick; 1087 stanza.attr.from = occupant.nick;
1056 self:broadcast_message(stanza); 1088 self:broadcast_message(stanza);
1295 for nick, occupant in self:each_occupant() do -- luacheck: ignore 213 1327 for nick, occupant in self:each_occupant() do -- luacheck: ignore 213
1296 if occupant.bare_jid == jid or ( 1328 if occupant.bare_jid == jid or (
1297 -- Outcast can be by host. 1329 -- Outcast can be by host.
1298 is_host_only and affiliation == "outcast" and select(2, jid_split(occupant.bare_jid)) == host 1330 is_host_only and affiliation == "outcast" and select(2, jid_split(occupant.bare_jid)) == host
1299 ) then 1331 ) then
1300 -- need to publcize in all cases; as affiliation in <item/> has changed. 1332 -- need to publicize in all cases; as affiliation in <item/> has changed.
1301 occupants_updated[occupant] = occupant.role; 1333 occupants_updated[occupant] = occupant.role;
1302 if occupant.role ~= role and ( 1334 if occupant.role ~= role and (
1303 is_downgrade or 1335 is_downgrade or
1304 valid_roles[occupant.role or "none"] < role_rank -- upgrade 1336 valid_roles[occupant.role or "none"] < role_rank -- upgrade
1305 ) then 1337 ) then
1322 1354
1323 if next(occupants_updated) ~= nil then 1355 if next(occupants_updated) ~= nil then
1324 for occupant, old_role in pairs(occupants_updated) do 1356 for occupant, old_role in pairs(occupants_updated) do
1325 self:publicise_occupant_status(occupant, x, nil, actor, reason); 1357 self:publicise_occupant_status(occupant, x, nil, actor, reason);
1326 if occupant.role == nil then 1358 if occupant.role == nil then
1327 module:fire_event("muc-occupant-left", {room = self; nick = occupant.nick; occupant = occupant;}); 1359 module:fire_event("muc-occupant-left", {
1360 room = self;
1361 nick = occupant.nick;
1362 occupant = occupant;
1363 });
1328 elseif is_semi_anonymous and 1364 elseif is_semi_anonymous and
1329 (old_role == "moderator" and occupant.role ~= "moderator") or 1365 (old_role == "moderator" and occupant.role ~= "moderator") or
1330 (old_role ~= "moderator" and occupant.role == "moderator") then -- Has gained or lost moderator status 1366 (old_role ~= "moderator" and occupant.role == "moderator") then -- Has gained or lost moderator status
1331 -- Send everyone else's presences (as jid visibility has changed) 1367 -- Send everyone else's presences (as jid visibility has changed)
1332 for real_jid in occupant:each_session() do 1368 for real_jid in occupant:each_session() do
1374 function room_mt:get_role(nick) 1410 function room_mt:get_role(nick)
1375 local occupant = self:get_occupant_by_nick(nick); 1411 local occupant = self:get_occupant_by_nick(nick);
1376 return occupant and occupant.role or nil; 1412 return occupant and occupant.role or nil;
1377 end 1413 end
1378 1414
1415 function room_mt:may_set_role(actor, occupant, role)
1416 local event = {
1417 room = self,
1418 actor = actor,
1419 occupant = occupant,
1420 role = role,
1421 };
1422
1423 module:fire_event("muc-pre-set-role", event);
1424 if event.allowed ~= nil then
1425 return event.allowed, event.error, event.condition;
1426 end
1427
1428 -- Can't do anything to other owners or admins
1429 local occupant_affiliation = self:get_affiliation(occupant.bare_jid);
1430 if occupant_affiliation == "owner" or occupant_affiliation == "admin" then
1431 return nil, "cancel", "not-allowed";
1432 end
1433
1434 -- If you are trying to give or take moderator role you need to be an owner or admin
1435 if occupant.role == "moderator" or role == "moderator" then
1436 local actor_affiliation = self:get_affiliation(actor);
1437 if actor_affiliation ~= "owner" and actor_affiliation ~= "admin" then
1438 return nil, "cancel", "not-allowed";
1439 end
1440 end
1441
1442 -- Need to be in the room and a moderator
1443 local actor_occupant = self:get_occupant_by_real_jid(actor);
1444 if not actor_occupant or actor_occupant.role ~= "moderator" then
1445 return nil, "cancel", "not-allowed";
1446 end
1447
1448 return true;
1449 end
1450
1379 function room_mt:set_role(actor, occupant_jid, role, reason) 1451 function room_mt:set_role(actor, occupant_jid, role, reason)
1380 if not actor then return nil, "modify", "not-acceptable"; end 1452 if not actor then return nil, "modify", "not-acceptable"; end
1381 1453
1382 local occupant = self:get_occupant_by_nick(occupant_jid); 1454 local occupant = self:get_occupant_by_nick(occupant_jid);
1383 if not occupant then return nil, "modify", "item-not-found"; end 1455 if not occupant then return nil, "modify", "item-not-found"; end
1388 role = role ~= "none" and role or nil; -- coerces `role == false` to `nil` 1460 role = role ~= "none" and role or nil; -- coerces `role == false` to `nil`
1389 1461
1390 if actor == true then 1462 if actor == true then
1391 actor = nil -- So we can pass it safely to 'publicise_occupant_status' below 1463 actor = nil -- So we can pass it safely to 'publicise_occupant_status' below
1392 else 1464 else
1393 -- Can't do anything to other owners or admins 1465 local allowed, err, condition = self:may_set_role(actor, occupant, role)
1394 local occupant_affiliation = self:get_affiliation(occupant.bare_jid); 1466 if not allowed then
1395 if occupant_affiliation == "owner" or occupant_affiliation == "admin" then 1467 return allowed, err, condition;
1396 return nil, "cancel", "not-allowed";
1397 end
1398
1399 -- If you are trying to give or take moderator role you need to be an owner or admin
1400 if occupant.role == "moderator" or role == "moderator" then
1401 local actor_affiliation = self:get_affiliation(actor);
1402 if actor_affiliation ~= "owner" and actor_affiliation ~= "admin" then
1403 return nil, "cancel", "not-allowed";
1404 end
1405 end
1406
1407 -- Need to be in the room and a moderator
1408 local actor_occupant = self:get_occupant_by_real_jid(actor);
1409 if not actor_occupant or actor_occupant.role ~= "moderator" then
1410 return nil, "cancel", "not-allowed";
1411 end 1468 end
1412 end 1469 end
1413 1470
1414 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}); 1471 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"});
1415 if not role then 1472 if not role then
1416 x:tag("status", {code = "307"}):up(); 1473 x:tag("status", {code = "307"}):up();
1417 end 1474 end
1475
1476 local prev_role = occupant.role;
1418 occupant.role = role; 1477 occupant.role = role;
1419 self:save_occupant(occupant); 1478 self:save_occupant(occupant);
1420 self:publicise_occupant_status(occupant, x, nil, actor, reason); 1479 self:publicise_occupant_status(occupant, x, nil, actor, reason, prev_role);
1421 if role == nil then 1480 if role == nil then
1422 module:fire_event("muc-occupant-left", {room = self; nick = occupant.nick; occupant = occupant;}); 1481 module:fire_event("muc-occupant-left", {
1482 room = self;
1483 nick = occupant.nick;
1484 occupant = occupant;
1485 });
1423 end 1486 end
1424 return true; 1487 return true;
1425 end 1488 end
1426 1489
1427 local whois = module:require "muc/whois"; 1490 local whois = module:require "muc/whois";