Software / code / prosody
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 |