Comparison

plugins/muc/mod_muc.lua @ 11200:bf8f2da84007

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Thu, 05 Nov 2020 22:31:25 +0100
parent 11057:13eee48071c8
child 11215:9ce0a899ff07
comparison
equal deleted inserted replaced
11199:6c7c50a4de32 11200:bf8f2da84007
84 local register = module:require "muc/register"; 84 local register = module:require "muc/register";
85 room_mt.get_registered_nick = register.get_registered_nick; 85 room_mt.get_registered_nick = register.get_registered_nick;
86 room_mt.get_registered_jid = register.get_registered_jid; 86 room_mt.get_registered_jid = register.get_registered_jid;
87 room_mt.handle_register_iq = register.handle_register_iq; 87 room_mt.handle_register_iq = register.handle_register_iq;
88 88
89 local presence_broadcast = module:require "muc/presence_broadcast";
90 room_mt.get_presence_broadcast = presence_broadcast.get;
91 room_mt.set_presence_broadcast = presence_broadcast.set;
92 room_mt.get_valid_broadcast_roles = presence_broadcast.get_valid_broadcast_roles;
93
94
89 local jid_split = require "util.jid".split; 95 local jid_split = require "util.jid".split;
96 local jid_prep = require "util.jid".prep;
90 local jid_bare = require "util.jid".bare; 97 local jid_bare = require "util.jid".bare;
91 local st = require "util.stanza"; 98 local st = require "util.stanza";
92 local cache = require "util.cache"; 99 local cache = require "util.cache";
93 local um_is_admin = require "core.usermanager".is_admin; 100 local um_is_admin = require "core.usermanager".is_admin;
94 101
96 103
97 module:depends("disco"); 104 module:depends("disco");
98 module:add_identity("conference", "text", module:get_option_string("name", "Prosody Chatrooms")); 105 module:add_identity("conference", "text", module:get_option_string("name", "Prosody Chatrooms"));
99 module:add_feature("http://jabber.org/protocol/muc"); 106 module:add_feature("http://jabber.org/protocol/muc");
100 module:depends "muc_unique" 107 module:depends "muc_unique"
108 module:require "muc/hats";
101 module:require "muc/lock"; 109 module:require "muc/lock";
102 110
103 local function is_admin(jid) 111 local function is_admin(jid)
104 return um_is_admin(jid, module.host); 112 return um_is_admin(jid, module.host);
105 end 113 end
127 local room_items_cache = {}; 135 local room_items_cache = {};
128 136
129 local function room_save(room, forced, savestate) 137 local function room_save(room, forced, savestate)
130 local node = jid_split(room.jid); 138 local node = jid_split(room.jid);
131 local is_persistent = persistent.get(room); 139 local is_persistent = persistent.get(room);
132 room_items_cache[room.jid] = room:get_public() and room:get_name() or nil; 140 if room:get_public() then
141 room_items_cache[room.jid] = room:get_name() or "";
142 else
143 room_items_cache[room.jid] = nil;
144 end
145
133 if is_persistent or savestate then 146 if is_persistent or savestate then
134 persistent_rooms:set(nil, room.jid, true); 147 persistent_rooms:set(nil, room.jid, true);
135 local data, state = room:freeze(savestate); 148 local data, state = room:freeze(savestate);
136 room_state:set(node, state); 149 room_state:set(node, state);
137 return room_configs:set(node, data); 150 return room_configs:set(node, data);
153 module:log("info", "Room limit of %d reached, no new rooms allowed", max_rooms); 166 module:log("info", "Room limit of %d reached, no new rooms allowed", max_rooms);
154 return false; 167 return false;
155 end 168 end
156 module:log("debug", "Evicting room %s", jid); 169 module:log("debug", "Evicting room %s", jid);
157 room_eviction(); 170 room_eviction();
158 room_items_cache[room.jid] = room:get_public() and room:get_name() or nil; 171 if room:get_public() then
172 room_items_cache[room.jid] = room:get_name() or "";
173 else
174 room_items_cache[room.jid] = nil;
175 end
159 local ok, err = room_save(room, nil, true); -- Force to disk 176 local ok, err = room_save(room, nil, true); -- Force to disk
160 if not ok then 177 if not ok then
161 module:log("error", "Failed to swap inactive room %s to disk: %s", jid, err); 178 module:log("error", "Failed to swap inactive room %s to disk: %s", jid, err);
162 return false; 179 return false;
163 end 180 end
183 return false; 200 return false;
184 end 201 end
185 202
186 local function handle_broken_room(room, origin, stanza) 203 local function handle_broken_room(room, origin, stanza)
187 module:log("debug", "Returning error from broken room %s", room.jid); 204 module:log("debug", "Returning error from broken room %s", room.jid);
188 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); 205 origin.send(st.error_reply(stanza, "wait", "internal-server-error", nil, room.jid));
189 return true; 206 return true;
190 end 207 end
191 208
192 local function restore_room(jid) 209 local function restore_room(jid)
193 local node = jid_split(jid); 210 local node = jid_split(jid);
262 room:set_whois(module:get_option_boolean("muc_room_default_public_jids", 279 room:set_whois(module:get_option_boolean("muc_room_default_public_jids",
263 room:get_whois() == "anyone") and "anyone" or "moderators"); 280 room:get_whois() == "anyone") and "anyone" or "moderators");
264 room:set_changesubject(module:get_option_boolean("muc_room_default_change_subject", room:get_changesubject())); 281 room:set_changesubject(module:get_option_boolean("muc_room_default_change_subject", room:get_changesubject()));
265 room:set_historylength(module:get_option_number("muc_room_default_history_length", room:get_historylength())); 282 room:set_historylength(module:get_option_number("muc_room_default_history_length", room:get_historylength()));
266 room:set_language(lang or module:get_option_string("muc_room_default_language")); 283 room:set_language(lang or module:get_option_string("muc_room_default_language"));
284 room:set_presence_broadcast(module:get_option("muc_room_default_presence_broadcast", room:get_presence_broadcast()));
267 end 285 end
268 286
269 function create_room(room_jid, config) 287 function create_room(room_jid, config)
288 if jid_bare(room_jid) ~= room_jid or not jid_prep(room_jid, true) then
289 return nil, "invalid-jid";
290 end
270 local exists = get_room_from_jid(room_jid); 291 local exists = get_room_from_jid(room_jid);
271 if exists then 292 if exists then
272 return nil, "room-exists"; 293 return nil, "room-exists";
273 end 294 end
274 local room = muclib.new_room(room_jid, config); 295 local room = muclib.new_room(room_jid, config);
323 module:hook("host-disco-items", function(event) 344 module:hook("host-disco-items", function(event)
324 local reply = event.reply; 345 local reply = event.reply;
325 module:log("debug", "host-disco-items called"); 346 module:log("debug", "host-disco-items called");
326 if next(room_items_cache) ~= nil then 347 if next(room_items_cache) ~= nil then
327 for jid, room_name in pairs(room_items_cache) do 348 for jid, room_name in pairs(room_items_cache) do
349 if room_name == "" then room_name = nil; end
328 reply:tag("item", { jid = jid, name = room_name }):up(); 350 reply:tag("item", { jid = jid, name = room_name }):up();
329 end 351 end
330 else 352 else
331 for room in all_rooms() do 353 for room in all_rooms() do
332 if not room:get_hidden() then 354 if not room:get_hidden() then
333 local jid, room_name = room.jid, room:get_name(); 355 local jid, room_name = room.jid, room:get_name();
334 room_items_cache[jid] = room_name; 356 room_items_cache[jid] = room_name or "";
335 reply:tag("item", { jid = jid, name = room_name }):up(); 357 reply:tag("item", { jid = jid, name = room_name }):up();
336 end 358 end
337 end 359 end
338 end 360 end
339 end); 361 end);
343 end, 1); 365 end, 1);
344 366
345 module:hook("muc-room-pre-create", function(event) 367 module:hook("muc-room-pre-create", function(event)
346 local origin, stanza = event.origin, event.stanza; 368 local origin, stanza = event.origin, event.stanza;
347 if not track_room(event.room) then 369 if not track_room(event.room) then
348 origin.send(st.error_reply(stanza, "wait", "resource-constraint")); 370 origin.send(st.error_reply(stanza, "wait", "resource-constraint", nil, module.host));
349 return true; 371 return true;
350 end 372 end
351 end, -1000); 373 end, -1000);
352 374
353 module:hook("muc-room-destroyed",function(event) 375 module:hook("muc-room-destroyed",function(event)
394 local user_jid = stanza.attr.from; 416 local user_jid = stanza.attr.from;
395 if not is_admin(user_jid) and not ( 417 if not is_admin(user_jid) and not (
396 restrict_room_creation == "local" and 418 restrict_room_creation == "local" and
397 select(2, jid_split(user_jid)) == host_suffix 419 select(2, jid_split(user_jid)) == host_suffix
398 ) then 420 ) then
399 origin.send(st.error_reply(stanza, "cancel", "not-allowed", "Room creation is restricted")); 421 origin.send(st.error_reply(stanza, "cancel", "not-allowed", "Room creation is restricted", module.host));
400 return true; 422 return true;
401 end 423 end
402 end); 424 end);
403 end 425 end
404 end 426 end
439 -- Allow the room to be recreated by admin or after time has passed 461 -- Allow the room to be recreated by admin or after time has passed
440 delete_room(room); 462 delete_room(room);
441 room = nil; 463 room = nil;
442 else 464 else
443 if stanza.attr.type ~= "error" then 465 if stanza.attr.type ~= "error" then
444 local reply = st.error_reply(stanza, "cancel", "gone", room._data.reason) 466 local reply = st.error_reply(stanza, "cancel", "gone", room._data.reason, module.host)
445 if room._data.newjid then 467 if room._data.newjid then
446 local uri = "xmpp:"..room._data.newjid.."?join"; 468 local uri = "xmpp:"..room._data.newjid.."?join";
447 reply:get_child("error"):child_with_name("gone"):text(uri); 469 reply:get_child("error"):child_with_name("gone"):text(uri);
448 end 470 end
449 event.origin.send(reply); 471 event.origin.send(reply);
452 end 474 end
453 end 475 end
454 476
455 if room == nil then 477 if room == nil then
456 -- Watch presence to create rooms 478 -- Watch presence to create rooms
457 if stanza.attr.type == nil and stanza.name == "presence" then 479 if not jid_prep(room_jid, true) then
480 origin.send(st.error_reply(stanza, "modify", "jid-malformed", nil, module.host));
481 return true;
482 end
483 if stanza.attr.type == nil and stanza.name == "presence" and stanza:get_child("x", "http://jabber.org/protocol/muc") then
458 room = muclib.new_room(room_jid); 484 room = muclib.new_room(room_jid);
459 return room:handle_first_presence(origin, stanza); 485 return room:handle_first_presence(origin, stanza);
460 elseif stanza.attr.type ~= "error" then 486 elseif stanza.attr.type ~= "error" then
461 origin.send(st.error_reply(stanza, "cancel", "item-not-found")); 487 origin.send(st.error_reply(stanza, "cancel", "item-not-found", nil, module.host));
462 return true; 488 return true;
463 else 489 else
464 return; 490 return;
465 end 491 end
466 elseif room == false then -- Error loading room 492 elseif room == false then -- Error loading room
467 origin.send(st.error_reply(stanza, "wait", "resource-constraint")); 493 origin.send(st.error_reply(stanza, "wait", "resource-constraint", nil, module.host));
468 return true; 494 return true;
469 end 495 end
470 return room[method](room, origin, stanza); 496 return room[method](room, origin, stanza);
471 end, -2) 497 end, -2)
472 end 498 end
481 do -- Ad-hoc commands 507 do -- Ad-hoc commands
482 module:depends "adhoc"; 508 module:depends "adhoc";
483 local t_concat = table.concat; 509 local t_concat = table.concat;
484 local adhoc_new = module:require "adhoc".new; 510 local adhoc_new = module:require "adhoc".new;
485 local adhoc_initial = require "util.adhoc".new_initial_data_form; 511 local adhoc_initial = require "util.adhoc".new_initial_data_form;
512 local adhoc_simple = require "util.adhoc".new_simple_form;
486 local array = require "util.array"; 513 local array = require "util.array";
487 local dataforms_new = require "util.dataforms".new; 514 local dataforms_new = require "util.dataforms".new;
488 515
489 local destroy_rooms_layout = dataforms_new { 516 local destroy_rooms_layout = dataforms_new {
490 title = "Destroy rooms"; 517 title = "Destroy rooms";
511 end); 538 end);
512 local destroy_rooms_desc = adhoc_new("Destroy Rooms", 539 local destroy_rooms_desc = adhoc_new("Destroy Rooms",
513 "http://prosody.im/protocol/muc#destroy", destroy_rooms_handler, "admin"); 540 "http://prosody.im/protocol/muc#destroy", destroy_rooms_handler, "admin");
514 541
515 module:provides("adhoc", destroy_rooms_desc); 542 module:provides("adhoc", destroy_rooms_desc);
516 end 543
544
545 local set_affiliation_layout = dataforms_new {
546 -- FIXME wordsmith title, instructions, labels etc
547 title = "Set affiliation";
548
549 { name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/muc#set-affiliation" };
550 { name = "room", type = "jid-single", required = true, label = "Room"};
551 { name = "jid", type = "jid-single", required = true, label = "JID"};
552 { name = "affiliation", type = "list-single", required = true, label = "Affiliation",
553 options = { "owner"; "admin"; "member"; "none"; "outcast"; },
554 };
555 { name = "reason", type = "text-single", "Reason", }
556 };
557
558 local set_affiliation_handler = adhoc_simple(set_affiliation_layout, function (fields, errors)
559 if errors then
560 local errmsg = {};
561 for field, err in pairs(errors) do
562 errmsg[#errmsg + 1] = field .. ": " .. err;
563 end
564 return { status = "completed", error = { message = t_concat(errmsg, "\n") } };
565 end
566
567 local room = get_room_from_jid(fields.room);
568 if not room then
569 return { status = "canceled", error = { message = "No such room"; }; };
570 end
571 local ok, err, condition = room:set_affiliation(true, fields.jid, fields.affiliation, fields.reason);
572
573 if not ok then
574 return { status = "canceled", error = { message = "Affiliation change failed: "..err..":"..condition; }; };
575 end
576
577 return { status = "completed", info = "Affiliation updated",
578 };
579 end);
580
581 local set_affiliation_desc = adhoc_new("Set affiliation in room",
582 "http://prosody.im/protocol/muc#set-affiliation", set_affiliation_handler, "admin");
583
584 module:provides("adhoc", set_affiliation_desc);
585 end