aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/muc
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/muc')
-rw-r--r--plugins/muc/hats.lib.lua19
-rw-r--r--plugins/muc/hidden.lib.lua8
-rw-r--r--plugins/muc/history.lib.lua6
-rw-r--r--plugins/muc/lock.lib.lua4
-rw-r--r--plugins/muc/members_only.lib.lua2
-rw-r--r--plugins/muc/mod_muc.lua98
-rw-r--r--plugins/muc/muc.lib.lua39
-rw-r--r--plugins/muc/occupant.lib.lua2
-rw-r--r--plugins/muc/occupant_id.lib.lua6
-rw-r--r--plugins/muc/password.lib.lua2
-rw-r--r--plugins/muc/persistent.lib.lua11
-rw-r--r--plugins/muc/presence_broadcast.lib.lua2
-rw-r--r--plugins/muc/register.lib.lua12
-rw-r--r--plugins/muc/request.lib.lua6
-rw-r--r--plugins/muc/restrict_pm.lib.lua119
-rw-r--r--plugins/muc/subject.lib.lua4
16 files changed, 245 insertions, 95 deletions
diff --git a/plugins/muc/hats.lib.lua b/plugins/muc/hats.lib.lua
index 358e5100..492dc72c 100644
--- a/plugins/muc/hats.lib.lua
+++ b/plugins/muc/hats.lib.lua
@@ -1,7 +1,10 @@
-local st = require "util.stanza";
+local st = require "prosody.util.stanza";
local muc_util = module:require "muc/util";
-local xmlns_hats = "xmpp:prosody.im/protocol/hats:1";
+local hats_compat = module:get_option_boolean("muc_hats_compat", true); -- COMPAT for pre-XEP namespace, TODO reconsider default for next release
+
+local xmlns_hats_legacy = "xmpp:prosody.im/protocol/hats:1";
+local xmlns_hats = "urn:xmpp:hats:0";
-- Strip any hats claimed by the client (to prevent spoofing)
muc_util.add_filtered_namespace(xmlns_hats);
@@ -13,14 +16,26 @@ module:hook("muc-build-occupant-presence", function (event)
local hats = aff_data and aff_data.hats;
if not hats then return; end
local hats_el;
+ local legacy_hats_el;
for hat_id, hat_data in pairs(hats) do
if hat_data.active then
if not hats_el then
hats_el = st.stanza("hats", { xmlns = xmlns_hats });
end
hats_el:tag("hat", { uri = hat_id, title = hat_data.title }):up();
+
+ if hats_compat then
+ if not hats_el then
+ legacy_hats_el = st.stanza("hats", { xmlns = xmlns_hats_legacy });
+ end
+ legacy_hats_el:tag("hat", { uri = hat_id, title = hat_data.title }):up();
+ end
end
end
if not hats_el then return; end
event.stanza:add_direct_child(hats_el);
+
+ if legacy_hats_el then
+ event.stanza:add_direct_child(legacy_hats_el);
+ end
end);
diff --git a/plugins/muc/hidden.lib.lua b/plugins/muc/hidden.lib.lua
index 153df21a..d24fa47e 100644
--- a/plugins/muc/hidden.lib.lua
+++ b/plugins/muc/hidden.lib.lua
@@ -8,7 +8,7 @@
--
local restrict_public = not module:get_option_boolean("muc_room_allow_public", true);
-local um_is_admin = require "core.usermanager".is_admin;
+module:default_permission(restrict_public and "prosody:admin" or "prosody:registered", ":create-public-room");
local function get_hidden(room)
return room._data.hidden;
@@ -22,8 +22,8 @@ local function set_hidden(room, hidden)
end
module:hook("muc-config-form", function(event)
- if restrict_public and not um_is_admin(event.actor, module.host) then
- -- Don't show option if public rooms are restricted and user is not admin of this host
+ if not module:may(":create-public-room", event.actor) then
+ -- Hide config option if this user is not allowed to create public rooms
return;
end
table.insert(event.form, {
@@ -36,7 +36,7 @@ module:hook("muc-config-form", function(event)
end, 100-9);
module:hook("muc-config-submitted/muc#roomconfig_publicroom", function(event)
- if restrict_public and not um_is_admin(event.actor, module.host) then
+ if not module:may(":create-public-room", event.actor) then
return; -- Not allowed
end
if set_hidden(event.room, not event.value) then
diff --git a/plugins/muc/history.lib.lua b/plugins/muc/history.lib.lua
index 075b1890..005bd1d8 100644
--- a/plugins/muc/history.lib.lua
+++ b/plugins/muc/history.lib.lua
@@ -8,11 +8,11 @@
--
local gettime = os.time;
-local datetime = require "util.datetime";
-local st = require "util.stanza";
+local datetime = require "prosody.util.datetime";
+local st = require "prosody.util.stanza";
local default_history_length = 20;
-local max_history_length = module:get_option_number("max_history_messages", math.huge);
+local max_history_length = module:get_option_integer("max_history_messages", math.huge, 0);
local function set_max_history_length(_max_history_length)
max_history_length = _max_history_length or math.huge;
diff --git a/plugins/muc/lock.lib.lua b/plugins/muc/lock.lib.lua
index 32f2647b..bb5bf82b 100644
--- a/plugins/muc/lock.lib.lua
+++ b/plugins/muc/lock.lib.lua
@@ -7,10 +7,10 @@
-- COPYING file in the source package for more information.
--
-local st = require "util.stanza";
+local st = require "prosody.util.stanza";
local lock_rooms = module:get_option_boolean("muc_room_locking", true);
-local lock_room_timeout = module:get_option_number("muc_room_lock_timeout", 300);
+local lock_room_timeout = module:get_option_period("muc_room_lock_timeout", "5 minutes");
local function lock(room)
module:fire_event("muc-room-locked", {room = room;});
diff --git a/plugins/muc/members_only.lib.lua b/plugins/muc/members_only.lib.lua
index b10dc120..4f4e88fa 100644
--- a/plugins/muc/members_only.lib.lua
+++ b/plugins/muc/members_only.lib.lua
@@ -7,7 +7,7 @@
-- COPYING file in the source package for more information.
--
-local st = require "util.stanza";
+local st = require "prosody.util.stanza";
local muc_util = module:require "muc/util";
local valid_affiliations = muc_util.valid_affiliations;
diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua
index 5873b1a2..1dc99f07 100644
--- a/plugins/muc/mod_muc.lua
+++ b/plugins/muc/mod_muc.lua
@@ -86,21 +86,26 @@ room_mt.get_registered_nick = register.get_registered_nick;
room_mt.get_registered_jid = register.get_registered_jid;
room_mt.handle_register_iq = register.handle_register_iq;
+local restrict_pm = module:require "muc/restrict_pm";
+room_mt.get_allow_pm = restrict_pm.get_allow_pm;
+room_mt.set_allow_pm = restrict_pm.set_allow_pm;
+room_mt.get_allow_modpm = restrict_pm.get_allow_modpm;
+room_mt.set_allow_modpm = restrict_pm.set_allow_modpm;
+
local presence_broadcast = module:require "muc/presence_broadcast";
room_mt.get_presence_broadcast = presence_broadcast.get;
room_mt.set_presence_broadcast = presence_broadcast.set;
-room_mt.get_valid_broadcast_roles = presence_broadcast.get_valid_broadcast_roles;
+room_mt.get_valid_broadcast_roles = presence_broadcast.get_valid_broadcast_roles; -- FIXME doesn't exist in the library
local occupant_id = module:require "muc/occupant_id";
room_mt.get_salt = occupant_id.get_room_salt;
room_mt.get_occupant_id = occupant_id.get_occupant_id;
-local jid_split = require "util.jid".split;
-local jid_prep = require "util.jid".prep;
-local jid_bare = require "util.jid".bare;
-local st = require "util.stanza";
-local cache = require "util.cache";
-local um_is_admin = require "core.usermanager".is_admin;
+local jid_split = require "prosody.util.jid".split;
+local jid_prep = require "prosody.util.jid".prep;
+local jid_bare = require "prosody.util.jid".bare;
+local st = require "prosody.util.stanza";
+local cache = require "prosody.util.cache";
module:require "muc/config_form_sections";
@@ -111,21 +116,26 @@ module:depends "muc_unique"
module:require "muc/hats";
module:require "muc/lock";
-local function is_admin(jid)
- return um_is_admin(jid, module.host);
-end
+module:default_permissions("prosody:admin", {
+ ":automatic-ownership";
+ ":create-room";
+ ":recreate-destroyed-room";
+});
+module:default_permissions("prosody:guest", {
+ ":list-rooms";
+});
-if module:get_option_boolean("component_admins_as_room_owners", true) then
+if module:get_option_boolean("component_admins_as_room_owners", false) then
-- Monkey patch to make server admins room owners
local _get_affiliation = room_mt.get_affiliation;
function room_mt:get_affiliation(jid)
- if is_admin(jid) then return "owner"; end
+ if module:could(":automatic-ownership", jid) then return "owner"; end
return _get_affiliation(self, jid);
end
local _set_affiliation = room_mt.set_affiliation;
function room_mt:set_affiliation(actor, jid, affiliation, reason, data)
- if affiliation ~= "owner" and is_admin(jid) then return nil, "modify", "not-acceptable"; end
+ if affiliation ~= "owner" and module:could(":automatic-ownership", jid) then return nil, "modify", "not-acceptable"; end
return _set_affiliation(self, actor, jid, affiliation, reason, data);
end
end
@@ -158,8 +168,8 @@ local function room_save(room, forced, savestate)
end
end
-local max_rooms = module:get_option_number("muc_max_rooms");
-local max_live_rooms = module:get_option_number("muc_room_cache_size", 100);
+local max_rooms = module:get_option_integer("muc_max_rooms", nil, 0);
+local max_live_rooms = module:get_option_integer("muc_room_cache_size", 100, 1);
local room_hit = module:measure("room_hit", "rate");
local room_miss = module:measure("room_miss", "rate")
@@ -281,15 +291,16 @@ local function set_room_defaults(room, lang)
room:set_public(module:get_option_boolean("muc_room_default_public", false));
room:set_persistent(module:get_option_boolean("muc_room_default_persistent", room:get_persistent()));
room:set_members_only(module:get_option_boolean("muc_room_default_members_only", room:get_members_only()));
- room:set_allow_member_invites(module:get_option_boolean("muc_room_default_allow_member_invites",
- room:get_allow_member_invites()));
+ room:set_allow_member_invites(module:get_option_boolean("muc_room_default_allow_member_invites", room:get_allow_member_invites()));
room:set_moderated(module:get_option_boolean("muc_room_default_moderated", room:get_moderated()));
- room:set_whois(module:get_option_boolean("muc_room_default_public_jids",
- room:get_whois() == "anyone") and "anyone" or "moderators");
+ room:set_whois(module:get_option_boolean("muc_room_default_public_jids", room:get_whois() == "anyone") and "anyone" or "moderators");
room:set_changesubject(module:get_option_boolean("muc_room_default_change_subject", room:get_changesubject()));
- room:set_historylength(module:get_option_number("muc_room_default_history_length", room:get_historylength()));
+ room:set_historylength(module:get_option_integer("muc_room_default_history_length", room:get_historylength(), 0));
room:set_language(lang or module:get_option_string("muc_room_default_language"));
- room:set_presence_broadcast(module:get_option("muc_room_default_presence_broadcast", room:get_presence_broadcast()));
+ room:set_presence_broadcast(module:get_option_enum("muc_room_default_presence_broadcast", room:get_presence_broadcast(), "visitor", "participant",
+ "moderator"));
+ room:set_allow_pm(module:get_option_enum("muc_room_default_allow_pm", room:get_allow_pm(), "visitor", "participant", "moderator"));
+ room:set_allow_modpm(module:get_option_boolean("muc_room_default_always_allow_moderator_pms", room:get_allow_modpm()));
end
function create_room(room_jid, config)
@@ -350,8 +361,12 @@ function each_room(live_only)
end
module:hook("host-disco-items", function(event)
- local reply = event.reply;
module:log("debug", "host-disco-items called");
+ if not module:could(":list-rooms", event) then
+ module:log("debug", "Returning empty room list to unauthorized request");
+ return;
+ end
+ local reply = event.reply;
if next(room_items_cache) ~= nil then
for jid, room_name in pairs(room_items_cache) do
if room_name == "" then room_name = nil; end
@@ -388,7 +403,7 @@ end);
if module:get_option_boolean("muc_tombstones", true) then
- local ttl = module:get_option_number("muc_tombstone_expiry", 86400 * 31);
+ local ttl = module:get_option_period("muc_tombstone_expiry", "31 days");
module:hook("muc-room-destroyed",function(event)
local room = event.room;
@@ -412,26 +427,15 @@ if module:get_option_boolean("muc_tombstones", true) then
end, -10);
end
-do
- local restrict_room_creation = module:get_option("restrict_room_creation");
- if restrict_room_creation == true then
- restrict_room_creation = "admin";
- end
- if restrict_room_creation then
- local host_suffix = module.host:gsub("^[^%.]+%.", "");
- module:hook("muc-room-pre-create", function(event)
- local origin, stanza = event.origin, event.stanza;
- local user_jid = stanza.attr.from;
- if not is_admin(user_jid) and not (
- restrict_room_creation == "local" and
- select(2, jid_split(user_jid)) == host_suffix
- ) then
- origin.send(st.error_reply(stanza, "cancel", "not-allowed", "Room creation is restricted", module.host));
- return true;
- end
- end);
+local restrict_room_creation = module:get_option_enum("restrict_room_creation", false, true, "local");
+module:default_permission(restrict_room_creation == true and "prosody:admin" or "prosody:registered", ":create-room");
+module:hook("muc-room-pre-create", function(event)
+ local origin, stanza = event.origin, event.stanza;
+ if restrict_room_creation ~= false and not module:may(":create-room", event) then
+ origin.send(st.error_reply(stanza, "cancel", "not-allowed", "Room creation is restricted", module.host));
+ return true;
end
-end
+end);
for event_name, method in pairs {
-- Normal room interactions
@@ -465,7 +469,7 @@ for event_name, method in pairs {
if room and room._data.destroyed then
if room._data.locked < os.time()
- or (is_admin(stanza.attr.from) and stanza.name == "presence" and stanza.attr.type == nil) then
+ or (module:may(":recreate-destroyed-room", event) and stanza.name == "presence" and stanza.attr.type == nil) then
-- Allow the room to be recreated by admin or after time has passed
delete_room(room);
room = nil;
@@ -516,10 +520,10 @@ do -- Ad-hoc commands
module:depends "adhoc";
local t_concat = table.concat;
local adhoc_new = module:require "adhoc".new;
- local adhoc_initial = require "util.adhoc".new_initial_data_form;
- local adhoc_simple = require "util.adhoc".new_simple_form;
- local array = require "util.array";
- local dataforms_new = require "util.dataforms".new;
+ local adhoc_initial = require "prosody.util.adhoc".new_initial_data_form;
+ local adhoc_simple = require "prosody.util.adhoc".new_simple_form;
+ local array = require "prosody.util.array";
+ local dataforms_new = require "prosody.util.dataforms".new;
local destroy_rooms_layout = dataforms_new {
title = "Destroy rooms";
diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua
index 01427dbe..b8f276cf 100644
--- a/plugins/muc/muc.lib.lua
+++ b/plugins/muc/muc.lib.lua
@@ -12,18 +12,18 @@ local pairs = pairs;
local next = next;
local setmetatable = setmetatable;
-local dataform = require "util.dataforms";
-local iterators = require "util.iterators";
-local jid_split = require "util.jid".split;
-local jid_bare = require "util.jid".bare;
-local jid_prep = require "util.jid".prep;
-local jid_join = require "util.jid".join;
-local jid_resource = require "util.jid".resource;
-local resourceprep = require "util.encodings".stringprep.resourceprep;
-local st = require "util.stanza";
-local base64 = require "util.encodings".base64;
-local hmac_sha256 = require "util.hashes".hmac_sha256;
-local new_id = require "util.id".medium;
+local dataform = require "prosody.util.dataforms";
+local iterators = require "prosody.util.iterators";
+local jid_split = require "prosody.util.jid".split;
+local jid_bare = require "prosody.util.jid".bare;
+local jid_prep = require "prosody.util.jid".prep;
+local jid_join = require "prosody.util.jid".join;
+local jid_resource = require "prosody.util.jid".resource;
+local resourceprep = require "prosody.util.encodings".stringprep.resourceprep;
+local st = require "prosody.util.stanza";
+local base64 = require "prosody.util.encodings".base64;
+local hmac_sha256 = require "prosody.util.hashes".hmac_sha256;
+local new_id = require "prosody.util.id".medium;
local log = module._log;
@@ -1079,7 +1079,10 @@ function room_mt:handle_admin_query_set_command(origin, stanza)
local reason = item:get_child_text("reason");
local success, errtype, err
if item.attr.affiliation and item.attr.jid and not item.attr.role then
- local registration_data;
+ local registration_data = self:get_affiliation_data(item.attr.jid) or {};
+ if reason then
+ registration_data.reason = reason;
+ end
if item.attr.nick then
local room_nick = self.jid.."/"..item.attr.nick;
local existing_occupant = self:get_occupant_by_nick(room_nick);
@@ -1088,7 +1091,7 @@ function room_mt:handle_admin_query_set_command(origin, stanza)
self:set_role(true, room_nick, nil, "This nickname is reserved");
end
module:log("debug", "Reserving %s for %s (%s)", item.attr.nick, item.attr.jid, item.attr.affiliation);
- registration_data = { reserved_nickname = item.attr.nick };
+ registration_data.reserved_nickname = item.attr.nick;
end
success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, reason, registration_data);
elseif item.attr.role and item.attr.nick and not item.attr.affiliation then
@@ -1119,9 +1122,13 @@ function room_mt:handle_admin_query_get_command(origin, stanza)
if (affiliation_rank >= valid_affiliations.admin and affiliation_rank >= _aff_rank)
or (self:get_members_only() and self:get_whois() == "anyone" and affiliation_rank >= valid_affiliations.member) then
local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin");
- for jid in self:each_affiliation(_aff or "none") do
+ for jid, _, data in self:each_affiliation(_aff or "none") do
local nick = self:get_registered_nick(jid);
- reply:tag("item", {affiliation = _aff, jid = jid, nick = nick }):up();
+ reply:tag("item", {affiliation = _aff, jid = jid, nick = nick });
+ if data and data.reason then
+ reply:text_tag("reason", data.reason);
+ end
+ reply:up();
end
origin.send(reply:up());
return true;
diff --git a/plugins/muc/occupant.lib.lua b/plugins/muc/occupant.lib.lua
index 8fe4bbdf..a7d9cef7 100644
--- a/plugins/muc/occupant.lib.lua
+++ b/plugins/muc/occupant.lib.lua
@@ -1,6 +1,6 @@
local pairs = pairs;
local setmetatable = setmetatable;
-local st = require "util.stanza";
+local st = require "prosody.util.stanza";
local util = module:require "muc/util";
local function get_filtered_presence(stanza)
diff --git a/plugins/muc/occupant_id.lib.lua b/plugins/muc/occupant_id.lib.lua
index 1d310b3d..b1081c9b 100644
--- a/plugins/muc/occupant_id.lib.lua
+++ b/plugins/muc/occupant_id.lib.lua
@@ -4,9 +4,9 @@
-- (C) 2020 Maxime “pep” Buquet <pep@bouah.net>
-- (C) 2020 Matthew Wild <mwild1@gmail.com>
-local uuid = require "util.uuid";
-local hmac_sha256 = require "util.hashes".hmac_sha256;
-local b64encode = require "util.encodings".base64.encode;
+local uuid = require "prosody.util.uuid";
+local hmac_sha256 = require "prosody.util.hashes".hmac_sha256;
+local b64encode = require "prosody.util.encodings".base64.encode;
local xmlns_occupant_id = "urn:xmpp:occupant-id:0";
diff --git a/plugins/muc/password.lib.lua b/plugins/muc/password.lib.lua
index dd3cb658..9d3c0cca 100644
--- a/plugins/muc/password.lib.lua
+++ b/plugins/muc/password.lib.lua
@@ -7,7 +7,7 @@
-- COPYING file in the source package for more information.
--
-local st = require "util.stanza";
+local st = require "prosody.util.stanza";
local function get_password(room)
return room._data.password;
diff --git a/plugins/muc/persistent.lib.lua b/plugins/muc/persistent.lib.lua
index c3b16ea4..29ed7784 100644
--- a/plugins/muc/persistent.lib.lua
+++ b/plugins/muc/persistent.lib.lua
@@ -8,7 +8,10 @@
--
local restrict_persistent = not module:get_option_boolean("muc_room_allow_persistent", true);
-local um_is_admin = require "core.usermanager".is_admin;
+module:default_permission(
+ restrict_persistent and "prosody:admin" or "prosody:registered",
+ ":create-persistent-room"
+);
local function get_persistent(room)
return room._data.persistent;
@@ -22,8 +25,8 @@ local function set_persistent(room, persistent)
end
module:hook("muc-config-form", function(event)
- if restrict_persistent and not um_is_admin(event.actor, module.host) then
- -- Don't show option if hidden rooms are restricted and user is not admin of this host
+ if not module:may(":create-persistent-room", event.actor) then
+ -- Hide config option if this user is not allowed to create persistent rooms
return;
end
table.insert(event.form, {
@@ -36,7 +39,7 @@ module:hook("muc-config-form", function(event)
end, 100-5);
module:hook("muc-config-submitted/muc#roomconfig_persistentroom", function(event)
- if restrict_persistent and not um_is_admin(event.actor, module.host) then
+ if not module:may(":create-persistent-room", event.actor) then
return; -- Not allowed
end
if set_persistent(event.room, event.value) then
diff --git a/plugins/muc/presence_broadcast.lib.lua b/plugins/muc/presence_broadcast.lib.lua
index 82a89fee..721c47aa 100644
--- a/plugins/muc/presence_broadcast.lib.lua
+++ b/plugins/muc/presence_broadcast.lib.lua
@@ -7,7 +7,7 @@
-- COPYING file in the source package for more information.
--
-local st = require "util.stanza";
+local st = require "prosody.util.stanza";
local valid_roles = { "none", "visitor", "participant", "moderator" };
local default_broadcast = {
diff --git a/plugins/muc/register.lib.lua b/plugins/muc/register.lib.lua
index 84045f33..82ccb8ea 100644
--- a/plugins/muc/register.lib.lua
+++ b/plugins/muc/register.lib.lua
@@ -1,8 +1,8 @@
-local jid_bare = require "util.jid".bare;
-local jid_resource = require "util.jid".resource;
-local resourceprep = require "util.encodings".stringprep.resourceprep;
-local st = require "util.stanza";
-local dataforms = require "util.dataforms";
+local jid_bare = require "prosody.util.jid".bare;
+local jid_resource = require "prosody.util.jid".resource;
+local resourceprep = require "prosody.util.encodings".stringprep.resourceprep;
+local st = require "prosody.util.stanza";
+local dataforms = require "prosody.util.dataforms";
local allow_unaffiliated = module:get_option_boolean("allow_unaffiliated_register", false);
@@ -94,8 +94,10 @@ local function enforce_nick_policy(event)
local nick = get_registered_nick(room, jid_bare(stanza.attr.from));
if nick then
if event.occupant then
+ -- someone is joining, force their nickname to the registered one
event.occupant.nick = jid_bare(event.occupant.nick) .. "/" .. nick;
elseif event.dest_occupant.nick ~= jid_bare(event.dest_occupant.nick) .. "/" .. nick then
+ -- someone is trying to change nickname to something other than their registered nickname, can't have that
module:log("debug", "Attempt by %s to join as %s, but their reserved nick is %s", stanza.attr.from, requested_nick, nick);
local reply = st.error_reply(stanza, "cancel", "not-acceptable", nil, room.jid):up();
origin.send(reply);
diff --git a/plugins/muc/request.lib.lua b/plugins/muc/request.lib.lua
index 4e95fdc3..f3786595 100644
--- a/plugins/muc/request.lib.lua
+++ b/plugins/muc/request.lib.lua
@@ -7,14 +7,14 @@
-- COPYING file in the source package for more information.
--
-local st = require "util.stanza";
-local jid_resource = require "util.jid".resource;
+local st = require "prosody.util.stanza";
+local jid_resource = require "prosody.util.jid".resource;
module:hook("muc-disco#info", function(event)
event.reply:tag("feature", {var = "http://jabber.org/protocol/muc#request"}):up();
end);
-local voice_request_form = require "util.dataforms".new({
+local voice_request_form = require "prosody.util.dataforms".new({
title = "Voice Request";
{
name = "FORM_TYPE";
diff --git a/plugins/muc/restrict_pm.lib.lua b/plugins/muc/restrict_pm.lib.lua
new file mode 100644
index 00000000..e0b25cc8
--- /dev/null
+++ b/plugins/muc/restrict_pm.lib.lua
@@ -0,0 +1,119 @@
+-- Based on code from mod_muc_restrict_pm in prosody-modules@d82c0383106a
+-- by Nicholas George <wirlaburla@worlio.com>
+
+local st = require "util.stanza";
+local muc_util = module:require "muc/util";
+local valid_roles = muc_util.valid_roles;
+
+-- COMPAT w/ prosody-modules allow_pm
+local compat_map = {
+ everyone = "visitor";
+ participants = "participant";
+ moderators = "moderator";
+ members = "affiliated";
+};
+
+local function get_allow_pm(room)
+ local val = room._data.allow_pm;
+ return compat_map[val] or val or "visitor";
+end
+
+local function set_allow_pm(room, val)
+ if get_allow_pm(room) == val then return false; end
+ room._data.allow_pm = val;
+ return true;
+end
+
+local function get_allow_modpm(room)
+ return room._data.allow_modpm or false;
+end
+
+local function set_allow_modpm(room, val)
+ if get_allow_modpm(room) == val then return false; end
+ room._data.allow_modpm = val;
+ return true;
+end
+
+module:hook("muc-config-form", function(event)
+ local pmval = get_allow_pm(event.room);
+ table.insert(event.form, {
+ name = 'muc#roomconfig_allowpm';
+ type = 'list-single';
+ label = 'Allow private messages from';
+ options = {
+ { value = 'visitor', label = 'Everyone', default = pmval == 'visitor' };
+ { value = 'participant', label = 'Participants', default = pmval == 'participant' };
+ { value = 'moderator', label = 'Moderators', default = pmval == 'moderator' };
+ { value = 'affiliated', label = "Members", default = pmval == "affiliated" };
+ { value = 'none', label = 'No one', default = pmval == 'none' };
+ }
+ });
+ table.insert(event.form, {
+ name = '{xmpp:prosody.im}muc#allow_modpm';
+ type = 'boolean';
+ label = 'Always allow private messages to moderators';
+ value = get_allow_modpm(event.room)
+ });
+end);
+
+module:hook("muc-config-submitted/muc#roomconfig_allowpm", function(event)
+ if set_allow_pm(event.room, event.value) then
+ event.status_codes["104"] = true;
+ end
+end);
+
+module:hook("muc-config-submitted/{xmpp:prosody.im}muc#allow_modpm", function(event)
+ if set_allow_modpm(event.room, event.value) then
+ event.status_codes["104"] = true;
+ end
+end);
+
+local who_restricted = {
+ none = "in this group";
+ participant = "from guests";
+ moderator = "from non-moderators";
+ affiliated = "from non-members";
+};
+
+module:hook("muc-private-message", function(event)
+ local stanza, room = event.stanza, event.room;
+ local from_occupant = room:get_occupant_by_nick(stanza.attr.from);
+ local to_occupant = room:get_occupant_by_nick(stanza.attr.to);
+
+ -- To self is always okay
+ if to_occupant.bare_jid == from_occupant.bare_jid then return; end
+
+ if get_allow_modpm(room) then
+ if to_occupant and to_occupant.role == 'moderator'
+ or from_occupant and from_occupant.role == "moderator" then
+ return; -- Allow to/from moderators
+ end
+ end
+
+ local pmval = get_allow_pm(room);
+
+ if pmval ~= "none" then
+ if pmval == "affiliated" and room:get_affiliation(from_occupant.bare_jid) then
+ return; -- Allow from affiliated users
+ elseif valid_roles[from_occupant.role] >= valid_roles[pmval] then
+ module:log("debug", "Allowing PM: %s(%d) >= %s(%d)", from_occupant.role, valid_roles[from_occupant.role], pmval, valid_roles[pmval]);
+ return; -- Allow from a permitted role
+ end
+ end
+
+ local msg = ("Private messages are restricted %s"):format(who_restricted[pmval]);
+ module:log("debug", "Blocking PM from %s %s: %s", from_occupant.role, stanza.attr.from, msg);
+
+ room:route_to_occupant(
+ from_occupant,
+ st.error_reply(stanza, "cancel", "policy-violation", msg, room.jid)
+ );
+ return false;
+end, 1);
+
+return {
+ get_allow_pm = get_allow_pm;
+ set_allow_pm = set_allow_pm;
+ get_allow_modpm = get_allow_modpm;
+ set_allow_modpm = set_allow_modpm;
+};
diff --git a/plugins/muc/subject.lib.lua b/plugins/muc/subject.lib.lua
index 3230817c..047ea2df 100644
--- a/plugins/muc/subject.lib.lua
+++ b/plugins/muc/subject.lib.lua
@@ -7,8 +7,8 @@
-- COPYING file in the source package for more information.
--
-local st = require "util.stanza";
-local dt = require "util.datetime";
+local st = require "prosody.util.stanza";
+local dt = require "prosody.util.datetime";
local muc_util = module:require "muc/util";
local valid_roles = muc_util.valid_roles;