From 985fd9a14a12618d1e3759426d55c02df2957d51 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 27 Mar 2014 19:16:13 -0400 Subject: plugins/muc: Massive refactor We now have occupant objects; you grab them, modify them, save them. New presence handling code. Modify all presence sending to go via new functions. --- plugins/muc/mod_muc.lua | 19 +- plugins/muc/muc.lib.lua | 779 +++++++++++++++++++++++-------------------- plugins/muc/occupant.lib.lua | 85 +++++ 3 files changed, 503 insertions(+), 380 deletions(-) create mode 100644 plugins/muc/occupant.lib.lua (limited to 'plugins') diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 8759cba4..a8a6388d 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -228,27 +228,14 @@ module.restore = function(data) hosts[module:get_host()].muc = { rooms = rooms }; end -function shutdown_room(room, stanza) - for nick, occupant in pairs(room._occupants) do - stanza.attr.from = nick; - for jid in pairs(occupant.sessions) do - stanza.attr.to = jid; - room:_route_stanza(stanza); - room._jid_nick[jid] = nil; - end - room._occupants[nick] = nil; - end -end function shutdown_component() if not saved then - local stanza = st.presence({type = "unavailable"}) - :tag("x", {xmlns = "http://jabber.org/protocol/muc#user"}) - :tag("item", { affiliation='none', role='none' }):up() + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) :tag("status", { code = "332"}):up(); for roomjid, room in pairs(rooms) do - shutdown_room(room, stanza); + room:clear(x); end - shutdown_room(host_room, stanza); + host_room:clear(x); end end module.unload = shutdown_component; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 27c50cd4..76fd7ee2 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -25,23 +25,9 @@ local setmetatable = setmetatable; local base64 = require "util.encodings".base64; local md5 = require "util.hashes".md5; -local default_history_length, max_history_length = 20, math.huge; +local occupant_lib = module:require "muc/occupant" -local get_filtered_presence do - local presence_filters = { - ["http://jabber.org/protocol/muc"] = true; - ["http://jabber.org/protocol/muc#user"] = true; - } - local function presence_filter(tag) - if presence_filters[tag.attr.xmlns] then - return nil; - end - return tag; - end - function get_filtered_presence(stanza) - return st.clone(stanza):maptags(presence_filter); - end -end +local default_history_length, max_history_length = 20, math.huge; local is_kickable_error do local kickable_error_conditions = { @@ -96,28 +82,103 @@ function room_mt:is_locked() return not not self.locked end -function room_mt:route_to_occupant(o_data, stanza) +--- Occupant functions +function room_mt:new_occupant(bare_real_jid, nick) + local occupant = occupant_lib.new(bare_real_jid, nick); + local affiliation = self:get_affiliation(bare_real_jid); + occupant.role = self:get_default_role(affiliation); + return occupant; +end + +function room_mt:get_occupant_by_nick(nick) + local occupant = self._occupants[nick]; + if occupant == nil then return nil end + return occupant_lib.copy(occupant); +end + +do + local function next_copied_occupant(occupants, occupant_jid) + local next_occupant_jid, raw_occupant = next(occupants, occupant_jid); + if next_occupant_jid == nil then return nil end + return next_occupant_jid, occupant_lib.copy(raw_occupant); + end + function room_mt:each_occupant(read_only) + return next_copied_occupant, self._occupants, nil; + end +end + +function room_mt:get_occupant_by_real_jid(real_jid) + local occupant_jid = self:get_occupant_jid(real_jid); + if occupant_jid == nil then return nil end + return self:get_occupant_by_nick(occupant_jid); +end + +function room_mt:save_occupant(occupant) + occupant = occupant_lib.copy(occupant); -- So that occupant can be modified more + local id = occupant.nick + + -- Need to maintain _jid_nick secondary index + local old_occupant = self._occupants[id]; + if old_occupant then + for real_jid in pairs(old_occupant.sessions) do + self._jid_nick[real_jid] = nil; + end + end + if occupant.role ~= nil and next(occupant.sessions) then + for real_jid, presence in occupant:each_session() do + self._jid_nick[real_jid] = occupant.nick; + end + else + occupant = nil + end + self._occupants[id] = occupant +end + +function room_mt:route_to_occupant(occupant, stanza) local to = stanza.attr.to; - for jid in pairs(o_data.sessions) do - stanza.attr.to = jid; - self:_route_stanza(stanza); + for jid, pr in occupant:each_session() do + if pr.attr.type ~= "unavailable" then + stanza.attr.to = jid; + self:route_stanza(stanza); + end end stanza.attr.to = to; end -function room_mt:broadcast_presence(stanza, sid, code, nick) - stanza = get_filtered_presence(stanza); - local occupant = self._occupants[stanza.attr.from]; - stanza:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) - :tag("item", {affiliation=occupant.affiliation or "none", role=occupant.role or "none", nick=nick}):up(); - if code then - stanza:tag("status", {code=code}):up(); +-- Adds an item to an "x" element. +-- actor is the attribute table +local function add_item(x, affiliation, role, jid, nick, actor, reason) + x:tag("item", {affiliation = affiliation; role = role; jid = jid; nick = nick;}) + if actor then + x:tag("actor", actor):up() + end + if reason then + x:tag("reason"):text(reason):up() + end + x:up(); + return x +end +-- actor is (real) jid +function room_mt:build_item_list(occupant, x, is_anonymous, nick, actor, reason) + local affiliation = self:get_affiliation(occupant.bare_jid); + local role = occupant.role; + local actor_jid = actor and self:get_occupant_jid(actor); + if actor then + actor = {nick = select(3,jid_split(actor_jid))}; + end + if is_anonymous then + add_item(x, affiliation, role, nil, nick, actor, reason); + else + if actor_jid then + actor.jid = actor_jid; + end + for real_jid, session in occupant:each_session() do + add_item(x, affiliation, role, real_jid, nick, actor, reason); + end end - self:broadcast_except_nick(stanza, stanza.attr.from); - stanza:tag("status", {code='110'}):up(); - stanza.attr.to = sid; - self:_route_stanza(stanza); + return x end + function room_mt:broadcast_message(stanza, historic) module:fire_event("muc-broadcast-message", {room = self, stanza = stanza, historic = historic}); self:broadcast(stanza); @@ -139,31 +200,89 @@ module:hook("muc-broadcast-message", function(event) t_insert(history, entry); while #history > room:get_historylength() do t_remove(history, 1) end end -end) - -function room_mt:broadcast_except_nick(stanza, nick) - return self:broadcast(stanza, function(rnick, occupant) return rnick ~= nick end) -end +end); -- Broadcast a stanza to all occupants in the room. --- optionally checks conditional called with nicl +-- optionally checks conditional called with (nick, occupant) function room_mt:broadcast(stanza, cond_func) - for nick, occupant in pairs(self._occupants) do + for nick, occupant in self:each_occupant() do if cond_func == nil or cond_func(nick, occupant) then self:route_to_occupant(occupant, stanza) end end end -function room_mt:send_occupant_list(to) - local current_nick = self:get_occupant_jid(to); - for occupant, o_data in pairs(self._occupants) do - if occupant ~= current_nick then - local pres = get_filtered_presence(o_data.sessions[o_data.jid]); - pres.attr.to, pres.attr.from = to, occupant; - pres:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) - :tag("item", {affiliation=o_data.affiliation or "none", role=o_data.role or "none"}):up(); - self:_route_stanza(pres); +-- Broadcasts an occupant's presence to the whole room +-- Takes (and modifies) the x element that goes into the stanzas +function room_mt:publicise_occupant_status(occupant, full_x, actor, reason) + local anon_x; + local has_anonymous = self:get_whois() ~= "anyone"; + if has_anonymous then + anon_x = st.clone(full_x); + self:build_item_list(occupant, anon_x, true, nil, actor, reason); + end + self:build_item_list(occupant,full_x, false, nil, actor, reason); + + -- General populance + local full_p + if occupant.role ~= nil then + -- Try to use main jid's presence + local pr = occupant:get_presence(); + if pr ~= nil then + full_p = st.clone(pr); + end + end + if full_p == nil then + full_p = st.presence{from=occupant.nick; type="unavailable"}; + end + local anon_p; + if has_anonymous then + anon_p = st.clone(full_p); + anon_p:add_child(anon_x); + end + full_p:add_child(full_x); + + for nick, n_occupant in self:each_occupant() do + if nick ~= occupant.nick or n_occupant.role == nil then + local pr = full_p; + if has_anonymous and n_occupant.role ~= "moderators" and occupant.bare_jid ~= n_occupant.bare_jid then + pr = anon_p; + end + self:route_to_occupant(n_occupant, pr); + end + end + + -- Presences for occupant itself + full_x:tag("status", {code = "110";}):up(); + if occupant.role == nil then + -- They get an unavailable + self:route_to_occupant(occupant, full_p); + else + -- use their own presences as templates + for full_jid, pr in occupant:each_session() do + if pr.attr.type ~= "unavailable" then + pr = st.clone(pr); + pr.attr.to = full_jid; + -- You can always see your own full jids + pr:add_child(full_x); + self:route_stanza(pr); + end + end + end +end + +function room_mt:send_occupant_list(to, filter) + local to_occupant = self:get_occupant_by_real_jid(to); + local has_anonymous = self:get_whois() ~= "anyone" + for occupant_jid, occupant in self:each_occupant() do + if filter and filter(occupant_jid, occupant) then + local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); + local is_anonymous = has_anonymous and occupant.role ~= "moderator" and to_occupant.bare_jid ~= occupant.bare_jid; + self:build_item_list(occupant, x, is_anonymous); + local pres = st.clone(occupant:get_presence()); + pres.attr.to = to; + pres:add_child(x); + self:route_stanza(pres); end end end @@ -248,12 +367,12 @@ function room_mt:send_history(stanza) } module:fire_event("muc-get-history", event) for msg in event.next_stanza , event do - self:_route_stanza(msg); + self:route_stanza(msg); end end function room_mt:get_disco_info(stanza) - local count = 0; for _ in pairs(self._occupants) do count = count + 1; end + local count = 0; for _ in self:each_occupant() do count = count + 1; end return st.reply(stanza):query("http://jabber.org/protocol/disco#info") :tag("identity", {category="conference", type="text", name=self:get_name()}):up() :tag("feature", {var="http://jabber.org/protocol/muc"}):up() @@ -272,7 +391,7 @@ function room_mt:get_disco_info(stanza) end function room_mt:get_disco_items(stanza) local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#items"); - for room_jid in pairs(self._occupants) do + for room_jid in self:each_occupant() do reply:tag("item", {jid = room_jid, name = room_jid:match("/(.*)")}):up(); end return reply; @@ -291,7 +410,7 @@ function room_mt:send_subject(to) local msg = create_subject_message(subject) msg.attr.from = from msg.attr.to = to - self:_route_stanza(msg); + self:route_stanza(msg); end end function room_mt:set_subject(current_nick, subject) @@ -306,14 +425,20 @@ function room_mt:set_subject(current_nick, subject) end function room_mt:handle_kickable(origin, stanza) + local real_jid = stanza.attr.from; + local occupant = self:get_occupant_by_real_jid(real_jid); + if occupant == nil then return nil; end local type, condition, text = stanza:get_error(); local error_message = "Kicked: "..(condition and condition:gsub("%-", " ") or "presence error"); if text then error_message = error_message..": "..text; end - local kick_stanza = st.presence({type='unavailable', from=stanza.attr.from, to=stanza.attr.to}) - :tag('status'):text(error_message); - self:handle_unavailable_to_occupant(origin, kick_stanza); -- send unavailable + occupant:set_session(real_jid, st.presence({type="unavailable"}) + :tag('status'):text(error_message)); + self:save_occupant(occupant); + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}) + :tag("status", {code = "307"}) + self:publicise_occupant_status(occupant, x); return true; end @@ -428,91 +553,26 @@ function room_mt:get_whois() return self._data.whois; end -function room_mt:handle_unavailable_to_occupant(origin, stanza) - local from = stanza.attr.from; - local current_nick = self:get_occupant_jid(from); - if not current_nick then - return true; -- discard - end - local pr = get_filtered_presence(stanza); - pr.attr.from = current_nick; - log("debug", "%s leaving %s", current_nick, self.jid); - self._jid_nick[from] = nil; - local occupant = self._occupants[current_nick]; - local new_jid = next(occupant.sessions); - if new_jid == from then new_jid = next(occupant.sessions, new_jid); end - if new_jid then - local jid = occupant.jid; - occupant.jid = new_jid; - occupant.sessions[from] = nil; - pr.attr.to = from; - pr:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) - :tag("item", {affiliation=occupant.affiliation or "none", role='none'}):up() - :tag("status", {code='110'}):up(); - self:_route_stanza(pr); - if jid ~= new_jid then - pr = st.clone(occupant.sessions[new_jid]) - :tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) - :tag("item", {affiliation=occupant.affiliation or "none", role=occupant.role or "none"}); - pr.attr.from = current_nick; - self:broadcast_except_nick(pr, current_nick); - end - else - occupant.role = 'none'; - self:broadcast_presence(pr, from); - self._occupants[current_nick] = nil; - module:fire_event("muc-occupant-left", { room = self; nick = current_nick; }); +module:hook("muc-room-pre-create", function(event) + local room = event.room; + if room:is_locked() and not event.stanza:get_child("x", "http://jabber.org/protocol/muc") then + room:unlock(); -- Older groupchat protocol doesn't lock end - return true; -end +end, 10); -function room_mt:handle_occupant_presence(origin, stanza) - local from = stanza.attr.from; - local pr = get_filtered_presence(stanza); - local current_nick = stanza.attr.to - pr.attr.from = current_nick; - log("debug", "%s broadcasted presence", current_nick); - self._occupants[current_nick].sessions[from] = pr; - self:broadcast_presence(pr, from); - return true; -end - -function room_mt:handle_change_nick(origin, stanza, current_nick, to) - local from = stanza.attr.from; - local occupant = self._occupants[current_nick]; - local is_multisession = next(occupant.sessions, next(occupant.sessions)); - if self._occupants[to] or is_multisession then - log("debug", "%s couldn't change nick", current_nick); - local reply = st.error_reply(stanza, "cancel", "conflict"):up(); - reply.tags[1].attr.code = "409"; - origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); - return true; - else - local to_nick = select(3, jid_split(to)); - log("debug", "%s (%s) changing nick to %s", current_nick, occupant.jid, to); - local p = st.presence({type='unavailable', from=current_nick}); - self:broadcast_presence(p, from, '303', to_nick); - self._occupants[current_nick] = nil; - self._occupants[to] = occupant; - self._jid_nick[from] = to; - local pr = get_filtered_presence(stanza); - pr.attr.from = to; - self._occupants[to].sessions[from] = pr; - self:broadcast_presence(pr, from); - return true; - end -end +-- Give the room creator owner affiliation +module:hook("muc-room-pre-create", function(event) + event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); +end, -1); module:hook("muc-occupant-pre-join", function(event) return module:fire_event("muc-occupant-pre-join/affiliation", event) or module:fire_event("muc-occupant-pre-join/password", event) - or module:fire_event("muc-occupant-pre-join/locked", event) - or module:fire_event("muc-occupant-pre-join/nick-conflict", event) + or module:fire_event("muc-occupant-pre-join/locked", event); end, -1) module:hook("muc-occupant-pre-join/password", function(event) local room, stanza = event.room, event.stanza; - local from, to = stanza.attr.from, stanza.attr.to; local password = stanza:get_child("x", "http://jabber.org/protocol/muc"); password = password and password:get_child_text("password", "http://jabber.org/protocol/muc"); if not password or password == "" then password = nil; end @@ -526,81 +586,19 @@ module:hook("muc-occupant-pre-join/password", function(event) end end, -1) -module:hook("muc-occupant-pre-join/nick-conflict", function(event) - local room, stanza = event.room, event.stanza; - local from, to = stanza.attr.from, stanza.attr.to; - local occupant = room._occupants[to] - if occupant -- occupant already exists - and jid_bare(from) ~= jid_bare(occupant.jid) then -- and has different bare real jid - log("debug", "%s couldn't join due to nick conflict: %s", from, to); - local reply = st.error_reply(stanza, "cancel", "conflict"):up(); - reply.tags[1].attr.code = "409"; - event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); - return true; - end -end, -1) - module:hook("muc-occupant-pre-join/locked", function(event) if event.room:is_locked() then -- Deny entry event.origin.send(st.error_reply(event.stanza, "cancel", "item-not-found")); return true; end -end, -1) - -function room_mt:handle_join(origin, stanza) - local from, to = stanza.attr.from, stanza.attr.to; - local affiliation = self:get_affiliation(from); - if affiliation == nil and next(self._affiliations) == nil then -- new room, no owners - affiliation = "owner"; - self._affiliations[jid_bare(from)] = affiliation; - if self:is_locked() and not stanza:get_child("x", "http://jabber.org/protocol/muc") then - self:unlock(); -- Older groupchat protocol doesn't lock - end - end - if module:fire_event("muc-occupant-pre-join", { - room = self; - origin = origin; - stanza = stanza; - affiliation = affiliation; - }) then return true; end - log("debug", "%s joining as %s", from, to); - - local role = self:get_default_role(affiliation) - if role then -- new occupant - local is_merge = not not self._occupants[to] - if not is_merge then - self._occupants[to] = {affiliation=affiliation, role=role, jid=from, sessions={[from]=get_filtered_presence(stanza)}}; - else - self._occupants[to].sessions[from] = get_filtered_presence(stanza); - end - self._jid_nick[from] = to; - self:send_occupant_list(from); - local pr = get_filtered_presence(stanza); - pr.attr.from = to; - pr:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) - :tag("item", {affiliation=affiliation or "none", role=role or "none"}):up(); - if not is_merge then - self:broadcast_except_nick(pr, to); - end - pr:tag("status", {code='110'}):up(); - if self:get_whois() == 'anyone' then - pr:tag("status", {code='100'}):up(); - end - if self:is_locked() then - pr:tag("status", {code='201'}):up(); - end - pr.attr.to = from; - self:_route_stanza(pr); - self:send_history(from, stanza); - self:send_subject(from); - return true; - end -end +end, -1); -- registration required for entering members-only room module:hook("muc-occupant-pre-join/affiliation", function(event) - if event.affiliation == nil and event.room:get_members_only() then - local reply = st.error_reply(event.stanza, "auth", "registration-required"):up(); + local room, stanza = event.room, event.stanza; + local affiliation = room:get_affiliation(stanza.attr.from); + if affiliation == nil and event.room:get_members_only() then + local reply = st.error_reply(stanza, "auth", "registration-required"):up(); reply.tags[1].attr.code = "407"; event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; @@ -609,43 +607,173 @@ end, -1) -- banned module:hook("muc-occupant-pre-join/affiliation", function(event) - if event.affiliation == "outcast" then - local reply = st.error_reply(event.stanza, "auth", "forbidden"):up(); + local room, stanza = event.room, event.stanza; + local affiliation = room:get_affiliation(stanza.attr.from); + if affiliation == "outcast" then + local reply = st.error_reply(stanza, "auth", "forbidden"):up(); reply.tags[1].attr.code = "403"; event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; end end, -1) -function room_mt:handle_available_to_occupant(origin, stanza) - local from, to = stanza.attr.from, stanza.attr.to; - local current_nick = self:get_occupant_jid(from); - if current_nick then - --if #pr == #stanza or current_nick ~= to then -- commented because google keeps resending directed presence - if current_nick == to then -- simple presence - return self:handle_occupant_presence(origin, stanza) - else -- change nick - return self:handle_change_nick(origin, stanza, current_nick, to) - end - --else -- possible rejoin - -- log("debug", "%s had connection replaced", current_nick); - -- self:handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}) - -- :tag('status'):text('Replaced by new connection'):up()); -- send unavailable - -- self:handle_to_occupant(origin, stanza); -- resend available - --end - else -- enter room - return self:handle_join(origin, stanza) - end -end +module:hook("muc-occupant-joined", function(event) + local room, stanza = event.room, event.stanza; + local real_jid = stanza.attr.from; + room:send_occupant_list(real_jid, function(nick, occupant) + -- Don't include self + return occupant.sessions[real_jid] == nil + end); + room:send_history(stanza); + room:send_subject(real_jid); +end, -1); function room_mt:handle_presence_to_occupant(origin, stanza) local type = stanza.attr.type; if type == "error" then -- error, kick em out! return self:handle_kickable(origin, stanza) - elseif type == "unavailable" then -- unavailable - return self:handle_unavailable_to_occupant(origin, stanza) - elseif not type then -- available - return self:handle_available_to_occupant(origin, stanza) + elseif type == nil or type == "unavailable" then + local real_jid = stanza.attr.from; + local bare_jid = jid_bare(real_jid); + local orig_occupant, dest_occupant; + local is_new_room = next(self._affiliations) == nil; + if is_new_room then + if type == "unavailable" then return true; end -- Unavailable from someone not in the room + if module:fire_event("muc-room-pre-create", { + room = self; + origin = origin; + stanza = stanza; + }) then return true; end + else + orig_occupant = self:get_occupant_by_real_jid(real_jid); + if type == "unavailable" and orig_occupant == nil then return true; end -- Unavailable from someone not in the room + end + local is_first_dest_session; + if type == "unavailable" then + -- dest_occupant = nil + elseif orig_occupant and orig_occupant.nick == stanza.attr.to then -- Just a presence update + log("debug", "presence update for %s from session %s", orig_occupant.nick, real_jid); + dest_occupant = orig_occupant; + else + local dest_jid = stanza.attr.to; + dest_occupant = self:get_occupant_by_nick(dest_jid); + if dest_occupant == nil then + log("debug", "no occupant found for %s; creating new occupant object for %s", dest_jid, real_jid); + is_first_dest_session = true; + dest_occupant = self:new_occupant(bare_jid, dest_jid); + else + is_first_dest_session = false; + end + end + local is_last_orig_session; + if orig_occupant ~= nil then + -- Is there are least 2 sessions? + is_last_orig_session = next(orig_occupant.sessions, next(orig_occupant.sessions)) == nil; + end + + local event, event_name = { + room = self; + origin = origin; + stanza = stanza; + is_first_session = is_first_dest_session; + is_last_session = is_last_orig_session; + }; + if orig_occupant == nil then + event_name = "muc-occupant-pre-join"; + event.is_new_room = is_new_room; + elseif dest_occupant == nil then + event_name = "muc-occupant-pre-leave"; + else + event_name = "muc-occupant-pre-change"; + end + if module:fire_event(event_name, event) then return true; end + + -- Check for nick conflicts + if dest_occupant ~= nil and not is_first_dest_session and bare_jid ~= jid_bare(dest_occupant.bare_jid) then -- new nick or has different bare real jid + log("debug", "%s couldn't join due to nick conflict: %s", real_jid, dest_occupant.nick); + local reply = st.error_reply(stanza, "cancel", "conflict"):up(); + reply.tags[1].attr.code = "409"; + origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); + return true; + end + + -- Send presence stanza about original occupant + if orig_occupant ~= nil and orig_occupant ~= dest_occupant then + local orig_x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}); + + if dest_occupant == nil then -- Session is leaving + log("debug", "session %s is leaving occupant %s", real_jid, orig_occupant.nick); + orig_occupant:set_session(real_jid, stanza); + else + log("debug", "session %s is changing from occupant %s to %s", real_jid, orig_occupant.nick, dest_occupant.nick); + orig_occupant:remove_session(real_jid); -- If we are moving to a new nick; we don't want to get our own presence + + local dest_nick = select(3, jid_split(dest_occupant.nick)); + local affiliation = self:get_affiliation(bare_jid); + + -- This session + if not is_first_dest_session then -- User is swapping into another pre-existing session + log("debug", "session %s is swapping into multisession %s, showing it leave.", real_jid, dest_occupant.nick); + -- Show the other session leaving + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}) + :tag("status"):text("you are joining pre-existing session " .. dest_nick):up(); + add_item(x, affiliation, "none"); + local pr = st.presence{from = dest_occupant.nick, to = real_jid, type = "unavailable"} + :add_child(x); + self:route_stanza(pr); + else + if is_last_orig_session then -- User is moving to a new session + log("debug", "no sessions in %s left; marking as nick change", orig_occupant.nick); + -- Everyone gets to see this as a nick change + local jid = self:get_whois() ~= "anyone" and real_jid or nil; -- FIXME: mods should see real jids + add_item(orig_x, affiliation, orig_occupant.role, jid, dest_nick); + orig_x:tag("status", {code = "303";}):up(); + end + end + -- The session itself always sees a nick change + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}); + add_item(x, affiliation, orig_occupant.role, real_jid, dest_nick); + -- self:build_item_list(orig_occupant, x, false); -- COMPAT + x:tag("status", {code = "303";}):up(); + x:tag("status", {code = "110";}):up(); + self:route_stanza(st.presence{from = dest_occupant.nick, to = real_jid, type = "unavailable"}:add_child(x)); + end + self:save_occupant(orig_occupant); + self:publicise_occupant_status(orig_occupant, orig_x); + + if is_last_orig_session then + module:fire_event("muc-occupant-left", {room = self; nick = orig_occupant.nick;}); + end + end + + if dest_occupant ~= nil then + dest_occupant:set_session(real_jid, stanza); + local dest_x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}); + if is_new_room then + dest_x:tag("status", {code = "201"}):up(); + end + if orig_occupant == nil and self:get_whois() == "anyone" then + dest_x:tag("status", {code = "100"}):up(); + end + self:save_occupant(dest_occupant); + self:publicise_occupant_status(dest_occupant, dest_x); + + if orig_occupant ~= nil and orig_occupant ~= dest_occupant and not is_last_orig_session then -- If user is swapping and wasn't last original session + log("debug", "session %s split nicks; showing %s rejoining", real_jid, orig_occupant.nick); + -- Show the original nick joining again + local pr = st.clone(orig_occupant:get_presence()); + pr.attr.to = real_jid; + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}); + self:build_item_list(orig_occupant, x, false); + -- TODO: new status code to inform client this was the multi-session it left? + pr:add_child(x); + self:route_stanza(pr); + end + + if orig_occupant == nil and is_first_dest_session then + module:fire_event("muc-occupant-joined", {room = self; nick = dest_occupant.nick; stanza = stanza;}); + end + end elseif type ~= 'result' then -- bad type if type ~= 'visible' and type ~= 'invisible' then -- COMPAT ejabberd can broadcast or forward XEP-0018 presences origin.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME correct error? @@ -659,14 +787,14 @@ function room_mt:handle_iq_to_occupant(origin, stanza) local type = stanza.attr.type; local id = stanza.attr.id; local current_nick = self:get_occupant_jid(from); - local o_data = self._occupants[to]; + local occupant = self:get_occupant_by_nick(to); if (type == "error" or type == "result") then do -- deconstruct_stanza_id - if not current_nick or not o_data then return nil; end + if not current_nick or not occupant then return nil; end local from_jid, id, to_jid_hash = (base64.decode(stanza.attr.id) or ""):match("^(.+)%z(.*)%z(.+)$"); if not(from == from_jid or from == jid_bare(from_jid)) then return nil; end local session_jid - for to_jid in pairs(o_data.sessions) do + for to_jid in occupant:each_session() do if md5(to_jid) == to_jid_hash then session_jid = to_jid; break; @@ -676,7 +804,7 @@ function room_mt:handle_iq_to_occupant(origin, stanza) stanza.attr.from, stanza.attr.to, stanza.attr.id = current_nick, session_jid, id end log("debug", "%s sent private iq stanza to %s (%s)", from, to, stanza.attr.to); - self:_route_stanza(stanza); + self:route_stanza(stanza); stanza.attr.from, stanza.attr.to, stanza.attr.id = from, to, id; return true; else -- Type is "get" or "set" @@ -684,19 +812,19 @@ function room_mt:handle_iq_to_occupant(origin, stanza) origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); return true; end - if not o_data then -- recipient not in room + if not occupant then -- recipient not in room origin.send(st.error_reply(stanza, "cancel", "item-not-found", "Recipient not in room")); return true; end do -- construct_stanza_id - stanza.attr.id = base64.encode(o_data.jid.."\0"..stanza.attr.id.."\0"..md5(from)); + stanza.attr.id = base64.encode(occupant.jid.."\0"..stanza.attr.id.."\0"..md5(from)); end - stanza.attr.from, stanza.attr.to = current_nick, o_data.jid; - log("debug", "%s sent private iq stanza to %s (%s)", from, to, o_data.jid); + stanza.attr.from, stanza.attr.to = current_nick, occupant.jid; + log("debug", "%s sent private iq stanza to %s (%s)", from, to, occupant.jid); if stanza.tags[1].attr.xmlns == 'vcard-temp' then stanza.attr.to = jid_bare(stanza.attr.to); end - self:_route_stanza(stanza); + self:route_stanza(stanza); stanza.attr.from, stanza.attr.to, stanza.attr.id = from, to, id; return true; end @@ -720,7 +848,7 @@ function room_mt:handle_message_to_occupant(origin, stanza) return self:handle_kickable(origin, stanza); -- send unavailable end - local o_data = self._occupants[to]; + local o_data = self:get_occupant_by_nick(to); if not o_data then origin.send(st.error_reply(stanza, "cancel", "item-not-found", "Recipient not in room")); return true; @@ -870,23 +998,29 @@ function room_mt:process_form(origin, stanza) end end -function room_mt:destroy(newjid, reason, password) - local pr = st.presence({type = "unavailable"}) - :tag("x", {xmlns = "http://jabber.org/protocol/muc#user"}) - :tag("item", { affiliation='none', role='none' }):up() - :tag("destroy", {jid=newjid}) - if reason then pr:tag("reason"):text(reason):up(); end - if password then pr:tag("password"):text(password):up(); end - for nick, occupant in pairs(self._occupants) do - pr.attr.from = nick; - for jid in pairs(occupant.sessions) do - pr.attr.to = jid; - self:_route_stanza(pr); - self._jid_nick[jid] = nil; - end - self._occupants[nick] = nil; - module:fire_event("muc-occupant-left", { room = self; nick = nick; }); +-- Removes everyone from the room +function room_mt:clear(x) + x = x or st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); + local occupants_updated = {}; + for nick, occupant in self:each_occupant() do + occupant.role = nil; + self:save_occupant(occupant); + occupants_updated[occupant] = true; end + for occupant in pairs(occupants_updated) do + self:publicise_occupant_status(occupant, x); + module:fire_event("muc-occupant-left", { room = self; nick = occupant.nick; }); + end +end + +function room_mt:destroy(newjid, reason, password) + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) + :tag("item", { affiliation='none', role='none' }):up() + :tag("destroy", {jid=newjid}); + if reason then x:tag("reason"):text(reason):up(); end + if password then x:tag("password"):text(password):up(); end + x:up(); + self:clear(x); self:set_persistent(false); module:fire_event("muc-room-destroyed", { room = self }); end @@ -911,7 +1045,7 @@ function room_mt:handle_admin_query_set_command(origin, stanza) end end if not item.attr.jid and item.attr.nick then -- COMPAT Workaround for Miranda sending 'nick' instead of 'jid' when changing affiliation - local occupant = self._occupants[self.jid.."/"..item.attr.nick]; + local occupant = self:get_occupant_by_nick(self.jid.."/"..item.attr.nick); if occupant then item.attr.jid = occupant.jid; end elseif not item.attr.nick and item.attr.jid then local nick = self:get_occupant_jid(item.attr.jid); @@ -958,18 +1092,7 @@ function room_mt:handle_admin_query_get_command(origin, stanza) local role = self:get_role(self:get_occupant_jid(actor)) or self:get_default_role(affiliation); if role == "moderator" then if _rol == "none" then _rol = nil; end - local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); - for occupant_jid, occupant in pairs(self._occupants) do - if occupant.role == _rol then - reply:tag("item", { - nick = select(3, jid_split(occupant_jid)), - role = _rol or "none", - affiliation = occupant.affiliation or "none", - jid = occupant.jid - }):up(); - end - end - origin.send(reply); + self:send_occupant_list(actor, function(occupant_jid, occupant) return occupant.role == _rol end); return true; else origin.send(st.error_reply(stanza, "auth", "forbidden")); @@ -1015,8 +1138,7 @@ end function room_mt:handle_groupchat_to_room(origin, stanza) local from = stanza.attr.from; - local current_nick = self:get_occupant_jid(from); - local occupant = self._occupants[current_nick]; + local occupant = self:get_occupant_by_real_jid(from); if not occupant then -- not in room origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); return true; @@ -1025,12 +1147,12 @@ function room_mt:handle_groupchat_to_room(origin, stanza) return true; else local from = stanza.attr.from; - stanza.attr.from = current_nick; + stanza.attr.from = occupant.nick; local subject = stanza:get_child_text("subject"); if subject then if occupant.role == "moderator" or ( self:get_changesubject() and occupant.role == "participant" ) then -- and participant - self:set_subject(current_nick, subject); + self:set_subject(occupant.nick, subject); else stanza.attr.from = from; origin.send(st.error_reply(stanza, "auth", "forbidden")); @@ -1096,7 +1218,7 @@ function room_mt:handle_mediated_invite(origin, stanza) end module:hook("muc-invite", function(event) - event.room:_route_stanza(event.stanza); + event.room:route_stanza(event.stanza); return true; end, -1) @@ -1210,64 +1332,46 @@ function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) end self._affiliations[jid] = affiliation; local role = self:get_default_role(affiliation); - local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) - :tag("item", {affiliation=affiliation or "none", role=role or "none"}) - :tag("reason"):text(reason or ""):up() - :up(); - local presence_type = nil; + local occupants_updated = {}; + for nick, occupant in self:each_occupant() do + if occupant.bare_jid == jid then + occupant.role = role; + self:save_occupant(occupant); + occupants_updated[occupant] = true; + end + end + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}); if not role then -- getting kicked - presence_type = "unavailable"; if affiliation == "outcast" then x:tag("status", {code="301"}):up(); -- banned else x:tag("status", {code="321"}):up(); -- affiliation change end end - local modified_nicks = {}; - for nick, occupant in pairs(self._occupants) do - if jid_bare(occupant.jid) == jid then - if not role then -- getting kicked - self._occupants[nick] = nil; - else - occupant.affiliation, occupant.role = affiliation, role; - end - for jid,pres in pairs(occupant.sessions) do -- remove for all sessions of the nick - if not role then self._jid_nick[jid] = nil; end - local p = st.clone(pres); - p.attr.from = nick; - p.attr.type = presence_type; - p.attr.to = jid; - p:add_child(x); - self:_route_stanza(p); - if occupant.jid == jid then - modified_nicks[nick] = p; - end - end - end + for occupant in pairs(occupants_updated) do + self:publicise_occupant_status(occupant, x, actor, reason); end if self.save then self:save(); end if callback then callback(); end - for nick,p in pairs(modified_nicks) do - p.attr.from = nick; - self:broadcast_except_nick(p, nick); - end return true; end function room_mt:get_role(nick) - local session = self._occupants[nick]; - return session and session.role or nil; + local occupant = self:get_occupant_by_nick(nick); + return occupant and occupant.role or nil; end function room_mt:can_set_role(actor_jid, occupant_jid, role) - local occupant = self._occupants[occupant_jid]; + local occupant = self:get_occupant_by_nick(occupant_jid); if not occupant or not actor_jid then return nil, "modify", "not-acceptable"; end if actor_jid == true then return true; end - local actor = self._occupants[self:get_occupant_jid(actor_jid)]; + local actor = self:get_occupant_by_real_jid(actor_jid); if actor.role == "moderator" then - if occupant.affiliation ~= "owner" and occupant.affiliation ~= "admin" then - if actor.affiliation == "owner" or actor.affiliation == "admin" then + local occupant_affiliation = self:get_affiliation(occupant.bare_jid) + local actor_affiliation = self:get_affiliation(actor.bare_jid) + if occupant_affiliation ~= "owner" and occupant_affiliation ~= "admin" then + if actor_affiliation == "owner" or actor_affiliation == "admin" then return true; elseif occupant.role ~= "moderator" and role ~= "moderator" then return true; @@ -1281,73 +1385,20 @@ function room_mt:set_role(actor, occupant_jid, role, callback, reason) if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end local allowed, err_type, err_condition = self:can_set_role(actor, occupant_jid, role); if not allowed then return allowed, err_type, err_condition; end - local occupant = self._occupants[occupant_jid]; - local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) - :tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"}) - :tag("reason"):text(reason or ""):up() - :up(); - local presence_type = nil; - if not role then -- kick - presence_type = "unavailable"; - self._occupants[occupant_jid] = nil; - for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick - self._jid_nick[jid] = nil; - end + + local occupant = self:get_occupant_by_nick(occupant_jid); + local occupant_affiliation = self:get_affiliation(occupant.bare_jid); + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}); + if not role then x:tag("status", {code = "307"}):up(); - else - occupant.role = role; - end - local bp; - for jid,pres in pairs(occupant.sessions) do -- send to all sessions of the nick - local p = st.clone(pres); - p.attr.from = occupant_jid; - p.attr.type = presence_type; - p.attr.to = jid; - p:add_child(x); - self:_route_stanza(p); - if occupant.jid == jid then - bp = p; - end end + occupant.role = role; + self:save_occupant(occupant); + self:publicise_occupant_status(occupant, x, actor, reason); if callback then callback(); end - if bp then - self:broadcast_except_nick(bp, occupant_jid); - end return true; end -function room_mt:_route_stanza(stanza) - local muc_child; - if stanza.name == "presence" then - local to_occupant = self._occupants[self:get_occupant_jid(stanza.attr.to)]; - local from_occupant = self._occupants[stanza.attr.from]; - if to_occupant and from_occupant then - if self:get_whois() == 'anyone' then - muc_child = stanza:get_child("x", "http://jabber.org/protocol/muc#user"); - else - if to_occupant.role == "moderator" or jid_bare(to_occupant.jid) == jid_bare(from_occupant.jid) then - muc_child = stanza:get_child("x", "http://jabber.org/protocol/muc#user"); - end - end - end - if muc_child then - for item in muc_child:childtags("item") do - if from_occupant == to_occupant then - item.attr.jid = stanza.attr.to; - else - item.attr.jid = from_occupant.jid; - end - end - end - end - self:route_stanza(stanza); - if muc_child then - for item in muc_child:childtags("item") do - item.attr.jid = nil; - end - end -end - local _M = {}; -- module "muc" function _M.new_room(jid, config) diff --git a/plugins/muc/occupant.lib.lua b/plugins/muc/occupant.lib.lua new file mode 100644 index 00000000..b4b12390 --- /dev/null +++ b/plugins/muc/occupant.lib.lua @@ -0,0 +1,85 @@ +local next = next; +local pairs = pairs; +local setmetatable = setmetatable; +local st = require "util.stanza"; + +local get_filtered_presence do + local presence_filters = { + ["http://jabber.org/protocol/muc"] = true; + ["http://jabber.org/protocol/muc#user"] = true; + } + local function presence_filter(tag) + if presence_filters[tag.attr.xmlns] then + return nil; + end + return tag; + end + function get_filtered_presence(stanza) + return st.clone(stanza):maptags(presence_filter); + end +end + +local occupant_mt = {}; +occupant_mt.__index = occupant_mt; + +local function new_occupant(bare_real_jid, nick) + return setmetatable({ + bare_jid = bare_real_jid; + nick = nick; -- in-room jid + sessions = {}; -- hash from real_jid to presence stanzas. stanzas should not be modified + role = nil; + jid = nil; -- Primary session + }, occupant_mt); +end + +-- Deep copy an occupant +local function copy_occupant(occupant) + local sessions = {}; + for full_jid, presence_stanza in pairs(occupant.sessions) do + if presence_stanza.attr.type ~= "unavailable" then + sessions[full_jid] = presence_stanza; + end + end + return setmetatable({ + bare_jid = occupant.bare_jid; + nick = occupant.nick; + sessions = sessions; + role = occupant.role; + jid = occupant.jid; + }, occupant_mt); +end + +function occupant_mt:set_session(real_jid, presence_stanza, replace_primary) + local pr = get_filtered_presence(presence_stanza); + pr.attr.from = self.nick; + pr.attr.to = real_jid; + + self.sessions[real_jid] = pr; + if replace_primary or self.jid == nil then + self.jid = real_jid; + end +end + +function occupant_mt:remove_session(real_jid) + -- Delete original session + local presence_stanza = self.sessions[real_jid]; + self.sessions[real_jid] = nil; + if self.jid == real_jid then + -- find another session to be the primary (might be nil) + self.jid = next(self.sessions); + end +end + +function occupant_mt:each_session() + return pairs(self.sessions) +end + +function occupant_mt:get_presence(real_jid) + return self.sessions[real_jid or self.jid] +end + +return { + new = new_occupant; + copy = copy_occupant; + mt = occupant_mt; +} -- cgit v1.2.3 From c7e768d9ac1447754a81b64633d14fd0190551e4 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 27 Mar 2014 19:18:57 -0400 Subject: plugins/muc/muc.lib: non-function changes (reordering, semicolons and comments) --- plugins/muc/muc.lib.lua | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 76fd7ee2..d96658f3 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -9,6 +9,9 @@ local select = select; local pairs, ipairs = pairs, ipairs; +local next = next; +local setmetatable = setmetatable; +local t_insert, t_remove = table.insert, table.remove; local gettime = os.time; local datetime = require "util.datetime"; @@ -20,8 +23,6 @@ local jid_bare = require "util.jid".bare; local jid_prep = require "util.jid".prep; local st = require "util.stanza"; local log = require "util.logger".init("mod_muc"); -local t_insert, t_remove = table.insert, table.remove; -local setmetatable = setmetatable; local base64 = require "util.encodings".base64; local md5 = require "util.hashes".md5; @@ -355,7 +356,7 @@ module:hook("muc-get-history", function(event) return msg end return true; -end) +end); function room_mt:send_history(stanza) local maxchars, maxstanzas, since = parse_history(stanza) @@ -584,7 +585,7 @@ module:hook("muc-occupant-pre-join/password", function(event) event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; end -end, -1) +end, -1); module:hook("muc-occupant-pre-join/locked", function(event) if event.room:is_locked() then -- Deny entry @@ -603,9 +604,9 @@ module:hook("muc-occupant-pre-join/affiliation", function(event) event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; end -end, -1) +end, -1); --- banned +-- check if user is banned module:hook("muc-occupant-pre-join/affiliation", function(event) local room, stanza = event.room, event.stanza; local affiliation = room:get_affiliation(stanza.attr.from); @@ -615,7 +616,7 @@ module:hook("muc-occupant-pre-join/affiliation", function(event) event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; end -end, -1) +end, -1); module:hook("muc-occupant-joined", function(event) local room, stanza = event.room, event.stanza; @@ -857,6 +858,7 @@ function room_mt:handle_message_to_occupant(origin, stanza) stanza:tag("x", { xmlns = "http://jabber.org/protocol/muc#user" }):up(); stanza.attr.from = current_nick; self:route_to_occupant(o_data, stanza) + -- TODO: Remove x tag? stanza.attr.from = from; return true; end @@ -1189,16 +1191,16 @@ function room_mt:handle_mediated_invite(origin, stanza) end local _invitee = jid_prep(payload.attr.to); if _invitee then + local _reason = payload:get_child_text("reason"); if self:get_whois() == "moderators" then _from = current_nick; end - local _reason = payload:get_child_text("reason") local invite = st.message({from = _to, to = _invitee, id = stanza.attr.id}) :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) :tag('invite', {from=_from}) :tag('reason'):text(_reason or ""):up() :up(); - local password = self:get_password() + local password = self:get_password(); if password then invite:tag("password"):text(password):up(); end @@ -1209,7 +1211,7 @@ function room_mt:handle_mediated_invite(origin, stanza) :tag('body') -- Add a plain message for clients which don't support invites :text(_from..' invited you to the room '.._to..(_reason and (' ('.._reason..')') or "")) :up(); - module:fire_event("muc-invite", { room = self, stanza = invite, origin = origin, incoming = stanza }); + module:fire_event("muc-invite", {room = self, stanza = invite, origin = origin, incoming = stanza}); return true; else origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); @@ -1232,7 +1234,7 @@ module:hook("muc-invite", function(event) log("debug", "%s invited %s into members only room %s, granting membership", from, invitee, room.jid); room:set_affiliation(from, invitee, "member", nil, "Invited by " .. current_nick) end -end) +end); function room_mt:handle_mediated_decline(origin, stanza) local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("decline") @@ -1289,13 +1291,11 @@ function room_mt:handle_message_to_room(origin, stanza) origin.send(st.error_reply(stanza, "cancel", "bad-request")); return true; end - else - return nil; end end function room_mt:route_stanza(stanza) - module:send(stanza) + module:send(stanza); end function room_mt:get_affiliation(jid) -- cgit v1.2.3 From b0999be824b03b48299a20bef4a62450dd3f3ae0 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 11:05:52 -0400 Subject: plugins/muc/muc.lib: Remove callback parameter from set_role and set_affiliation --- plugins/muc/muc.lib.lua | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index d96658f3..79252778 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1054,20 +1054,18 @@ function room_mt:handle_admin_query_set_command(origin, stanza) if nick then item.attr.nick = select(3, jid_split(nick)); end end local actor = stanza.attr.from; - local callback = function() origin.send(st.reply(stanza)); end 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 success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, callback, reason); - if not success then origin.send(st.error_reply(stanza, errtype, err)); end - return true; + success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, reason); elseif item.attr.role and item.attr.nick and not item.attr.affiliation then - local success, errtype, err = self:set_role(actor, self.jid.."/"..item.attr.nick, item.attr.role, callback, reason); - if not success then origin.send(st.error_reply(stanza, errtype, err)); end - return true; + success, errtype, err = self:set_role(actor, self.jid.."/"..item.attr.nick, item.attr.role, reason); else - origin.send(st.error_reply(stanza, "cancel", "bad-request")); - return true; + success, errtype, err = nil, "cancel", "bad-request"; end + if not success then origin.send(st.error_reply(stanza, errtype, err)); end + origin.send(st.reply(stanza)); + return true; end function room_mt:handle_admin_query_get_command(origin, stanza) @@ -1232,7 +1230,7 @@ module:hook("muc-invite", function(event) local from = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite").attr.from local current_nick = room:get_occupant_jid(from) log("debug", "%s invited %s into members only room %s, granting membership", from, invitee, room.jid); - room:set_affiliation(from, invitee, "member", nil, "Invited by " .. current_nick) + room:set_affiliation(from, invitee, "member", "Invited by " .. current_nick) end end); @@ -1305,7 +1303,7 @@ function room_mt:get_affiliation(jid) if not result and self._affiliations[host] == "outcast" then result = "outcast"; end -- host banned return result; end -function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) +function room_mt:set_affiliation(actor, jid, affiliation, reason) jid = jid_bare(jid); if affiliation == "none" then affiliation = nil; end if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then @@ -1315,7 +1313,6 @@ function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) local actor_affiliation = self:get_affiliation(actor); local target_affiliation = self:get_affiliation(jid); if target_affiliation == affiliation then -- no change, shortcut - if callback then callback(); end return true; end if actor_affiliation ~= "owner" then @@ -1352,7 +1349,6 @@ function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) self:publicise_occupant_status(occupant, x, actor, reason); end if self.save then self:save(); end - if callback then callback(); end return true; end @@ -1380,7 +1376,7 @@ function room_mt:can_set_role(actor_jid, occupant_jid, role) end return nil, "cancel", "not-allowed"; end -function room_mt:set_role(actor, occupant_jid, role, callback, reason) +function room_mt:set_role(actor, occupant_jid, role, reason) if role == "none" then role = nil; end if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end local allowed, err_type, err_condition = self:can_set_role(actor, occupant_jid, role); @@ -1395,7 +1391,6 @@ function room_mt:set_role(actor, occupant_jid, role, callback, reason) occupant.role = role; self:save_occupant(occupant); self:publicise_occupant_status(occupant, x, actor, reason); - if callback then callback(); end return true; end -- cgit v1.2.3 From 6334074f11baac6bd8101e205f038e8494553bfe Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 13:05:36 -0400 Subject: plugins/muc/muc.lib: Clean up :set_role. Removes :can_set_role --- plugins/muc/muc.lib.lua | 58 +++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 23 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 79252778..b3c1c174 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1356,34 +1356,46 @@ function room_mt:get_role(nick) local occupant = self:get_occupant_by_nick(nick); return occupant and occupant.role or nil; end -function room_mt:can_set_role(actor_jid, occupant_jid, role) + +local valid_roles = { + none = true; + visitor = true; + participant = true; + moderator = true; +} +function room_mt:set_role(actor, occupant_jid, role, reason) + if not actor then return nil, "modify", "not-acceptable"; end + local occupant = self:get_occupant_by_nick(occupant_jid); - if not occupant or not actor_jid then return nil, "modify", "not-acceptable"; end - - if actor_jid == true then return true; end - - local actor = self:get_occupant_by_real_jid(actor_jid); - if actor.role == "moderator" then - local occupant_affiliation = self:get_affiliation(occupant.bare_jid) - local actor_affiliation = self:get_affiliation(actor.bare_jid) - if occupant_affiliation ~= "owner" and occupant_affiliation ~= "admin" then - if actor_affiliation == "owner" or actor_affiliation == "admin" then - return true; - elseif occupant.role ~= "moderator" and role ~= "moderator" then - return true; + if not occupant then return nil, "modify", "not-acceptable"; end + + if valid_roles[role or "none"] == nil then + return nil, "modify", "not-acceptable"; + end + role = role ~= "none" and role or nil; -- coerces `role == false` to `nil` + + if actor ~= true then + -- Can't do anything to other owners or admins + local occupant_affiliation = self:get_affiliation(occupant.bare_jid); + if occupant_affiliation == "owner" and occupant_affiliation == "admin" then + return nil, "cancel", "not-allowed"; + end + + -- If you are trying to give or take moderator role you need to be an owner or admin + if occupant.role == "moderator" or role == "moderator" then + local actor_affiliation = self:get_affiliation(actor); + if actor_affiliation ~= "owner" and actor_affiliation ~= "admin" then + return nil, "cancel", "not-allowed"; end end + + -- Need to be in the room and a moderator + local actor_occupant = self:get_occupant_by_real_jid(actor); + if not actor_occupant or actor_occupant.role ~= "moderator" then + return nil, "cancel", "not-allowed"; + end end - return nil, "cancel", "not-allowed"; -end -function room_mt:set_role(actor, occupant_jid, role, reason) - if role == "none" then role = nil; end - if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end - local allowed, err_type, err_condition = self:can_set_role(actor, occupant_jid, role); - if not allowed then return allowed, err_type, err_condition; end - local occupant = self:get_occupant_by_nick(occupant_jid); - local occupant_affiliation = self:get_affiliation(occupant.bare_jid); local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}); if not role then x:tag("status", {code = "307"}):up(); -- cgit v1.2.3 From 46f318fa4f74a2194d98e8b082c97f7a62296077 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 13:11:11 -0400 Subject: plugins/muc/muc.lib: Fix sending occupant jid instead of real jid in actor --- plugins/muc/muc.lib.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index b3c1c174..0d1e4170 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -163,18 +163,18 @@ end function room_mt:build_item_list(occupant, x, is_anonymous, nick, actor, reason) local affiliation = self:get_affiliation(occupant.bare_jid); local role = occupant.role; - local actor_jid = actor and self:get_occupant_jid(actor); + local actor_attr; if actor then - actor = {nick = select(3,jid_split(actor_jid))}; + actor_attr = {nick = select(3,jid_split(self:get_occupant_jid(actor)))}; end if is_anonymous then - add_item(x, affiliation, role, nil, nick, actor, reason); + add_item(x, affiliation, role, nil, nick, actor_attr, reason); else - if actor_jid then - actor.jid = actor_jid; + if actor_attr then + actor_attr.jid = actor; end for real_jid, session in occupant:each_session() do - add_item(x, affiliation, role, real_jid, nick, actor, reason); + add_item(x, affiliation, role, real_jid, nick, actor_attr, reason); end end return x -- cgit v1.2.3 From f9f2d557fa2db46dbdbc510c4a326be26b237e52 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 13:14:33 -0400 Subject: plugins/muc/muc.lib: Allow `:send_occupant_list` to have no filter --- plugins/muc/muc.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 0d1e4170..b5b8bc97 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -276,7 +276,7 @@ function room_mt:send_occupant_list(to, filter) local to_occupant = self:get_occupant_by_real_jid(to); local has_anonymous = self:get_whois() ~= "anyone" for occupant_jid, occupant in self:each_occupant() do - if filter and filter(occupant_jid, occupant) then + if filter == nil or filter(occupant_jid, occupant) then local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); local is_anonymous = has_anonymous and occupant.role ~= "moderator" and to_occupant.bare_jid ~= occupant.bare_jid; self:build_item_list(occupant, x, is_anonymous); -- cgit v1.2.3 From 97759d2f65c1578f8a0cb030fb4ea83ff8d45682 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 13:34:46 -0400 Subject: plugins/muc/muc.lib: Fix anonymous check in `send_occupant_list` --- plugins/muc/muc.lib.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index b5b8bc97..02a2dc37 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -273,13 +273,21 @@ function room_mt:publicise_occupant_status(occupant, full_x, actor, reason) end function room_mt:send_occupant_list(to, filter) - local to_occupant = self:get_occupant_by_real_jid(to); - local has_anonymous = self:get_whois() ~= "anyone" + local to_bare = jid_bare(to); + local is_anonymous = true; + if self:get_whois() ~= "anyone" then + local affiliation = self:get_affiliation(to); + if affiliation ~= "admin" and affiliation ~= "owner" then + local occupant = self:get_occupant_by_real_jid(to); + if not occupant or occupant.role ~= "moderator" then + is_anonymous = false; + end + end + end for occupant_jid, occupant in self:each_occupant() do if filter == nil or filter(occupant_jid, occupant) then local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); - local is_anonymous = has_anonymous and occupant.role ~= "moderator" and to_occupant.bare_jid ~= occupant.bare_jid; - self:build_item_list(occupant, x, is_anonymous); + self:build_item_list(occupant, x, is_anonymous and to_bare ~= occupant.bare_jid); -- can always see your own jids local pres = st.clone(occupant:get_presence()); pres.attr.to = to; pres:add_child(x); -- cgit v1.2.3 From 09dc06b54e9f277d0fc9a0a92c68f20e7a839a75 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 14:15:18 -0400 Subject: plugins/muc/muc.lib: Smarter validation in set_affiliation --- plugins/muc/muc.lib.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 02a2dc37..d001800d 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1311,12 +1311,24 @@ function room_mt:get_affiliation(jid) if not result and self._affiliations[host] == "outcast" then result = "outcast"; end -- host banned return result; end + +local valid_affiliations = { + outcast = true; + none = true; + member = true; + admin = true; + owner = true; +}; function room_mt:set_affiliation(actor, jid, affiliation, reason) + if not actor then return nil, "modify", "not-acceptable"; end; + jid = jid_bare(jid); - if affiliation == "none" then affiliation = nil; end - if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then + + if valid_affiliations[affiliation or "none"] == nil then return nil, "modify", "not-acceptable"; end + affiliation = affiliation ~= "none" and affiliation or nil; -- coerces `affiliation == false` to `nil` + if actor ~= true then local actor_affiliation = self:get_affiliation(actor); local target_affiliation = self:get_affiliation(jid); -- cgit v1.2.3 From f090e4eddaf71cbe01a3e6810961a8492a70549d Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 17:58:25 -0400 Subject: plugins/muc/muc.lib: Improve set affiliation logic; - Each affiliation and role is now ranked - Changes up in affiliation will not downgrade your role - Now sends a new set of presences if you gained moderator in a semi-anonymous room. - Better input validation; matches closer with ':set_role' - Don't short circuit; as if user has non-default role they will not get updated --- plugins/muc/muc.lib.lua | 96 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 32 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index d001800d..9ae3f56a 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -60,6 +60,21 @@ function room_mt:get_occupant_jid(real_jid) return self._jid_nick[real_jid] end +local valid_affiliations = { + outcast = 0; + none = 1; + member = 2; + admin = 3; + owner = 4; +}; + +local valid_roles = { + none = 0; + visitor = 1; + participant = 2; + moderator = 3; +}; + function room_mt:get_default_role(affiliation) if affiliation == "owner" or affiliation == "admin" then return "moderator"; @@ -1312,13 +1327,6 @@ function room_mt:get_affiliation(jid) return result; end -local valid_affiliations = { - outcast = true; - none = true; - member = true; - admin = true; - owner = true; -}; function room_mt:set_affiliation(actor, jid, affiliation, reason) if not actor then return nil, "modify", "not-acceptable"; end; @@ -1329,34 +1337,52 @@ function room_mt:set_affiliation(actor, jid, affiliation, reason) end affiliation = affiliation ~= "none" and affiliation or nil; -- coerces `affiliation == false` to `nil` + local target_affiliation = self._affiliations[jid]; -- Raw; don't want to check against host + local is_downgrade = valid_affiliations[target_affiliation or "none"] > valid_affiliations[affiliation or "none"]; + if actor ~= true then - local actor_affiliation = self:get_affiliation(actor); - local target_affiliation = self:get_affiliation(jid); - if target_affiliation == affiliation then -- no change, shortcut - return true; - end - if actor_affiliation ~= "owner" then - if affiliation == "owner" or affiliation == "admin" or actor_affiliation ~= "admin" or target_affiliation == "owner" or target_affiliation == "admin" then - return nil, "cancel", "not-allowed"; - end - elseif target_affiliation == "owner" and jid_bare(actor) == jid then -- self change - local is_last = true; - for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end - if is_last then - return nil, "cancel", "conflict"; + local actor_bare = jid_bare(actor); + local actor_affiliation = self._affiliations[actor_bare]; + if actor_affiliation == "owner" then + if actor_bare == jid then -- self change + -- need at least one owner + local is_last = true; + for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end + if is_last then + return nil, "cancel", "conflict"; + end end + -- owners can do anything else + elseif affiliation == "owner" or affiliation == "admin" + or actor_affiliation ~= "admin" + or target_affiliation == "owner" or target_affiliation == "admin" then + -- Can't demote owners or other admins + return nil, "cancel", "not-allowed"; end end + + -- Set in 'database' self._affiliations[jid] = affiliation; + + -- Update roles local role = self:get_default_role(affiliation); - local occupants_updated = {}; + local role_rank = valid_roles[role or "none"]; + local occupants_updated = {}; -- Filled with old roles for nick, occupant in self:each_occupant() do if occupant.bare_jid == jid then - occupant.role = role; - self:save_occupant(occupant); - occupants_updated[occupant] = true; + -- need to publcize in all cases; as affiliation in has changed. + occupants_updated[occupant] = occupant.role; + if occupant.role ~= role and ( + is_downgrade or + valid_roles[occupant.role or "none"] < role_rank -- upgrade + ) then + occupant.role = role; + self:save_occupant(occupant); + end end end + + -- Tell the room of the new occupant affiliations+roles local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}); if not role then -- getting kicked if affiliation == "outcast" then @@ -1365,9 +1391,21 @@ function room_mt:set_affiliation(actor, jid, affiliation, reason) x:tag("status", {code="321"}):up(); -- affiliation change end end - for occupant in pairs(occupants_updated) do + local is_semi_anonymous = self:get_whois() == "moderators"; + for occupant, old_role in pairs(occupants_updated) do self:publicise_occupant_status(occupant, x, actor, reason); + if is_semi_anonymous and + (old_role == "moderator" and occupant.role ~= "moderator") or + (old_role ~= "moderator" and occupant.role == "moderator") then -- Has gained or lost moderator status + -- Send everyone else's presences (as jid visibility has changed) + for real_jid in occupant:each_session() do + self:send_occupant_list(real_jid, function(occupant_jid, occupant) + return occupant.bare_jid ~= jid; + end); + end + end end + if self.save then self:save(); end return true; end @@ -1377,12 +1415,6 @@ function room_mt:get_role(nick) return occupant and occupant.role or nil; end -local valid_roles = { - none = true; - visitor = true; - participant = true; - moderator = true; -} function room_mt:set_role(actor, occupant_jid, role, reason) if not actor then return nil, "modify", "not-acceptable"; end -- cgit v1.2.3 From ac04e20d3bcd37432c086d4b91222914c7c4ed59 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 18:33:38 -0400 Subject: plugins/muc/muc.lib: Status codes should be inside of x element --- plugins/muc/muc.lib.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 9ae3f56a..08ca0384 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1013,8 +1013,9 @@ function room_mt:process_form(origin, stanza) if next(changed) then local msg = st.message({type='groupchat', from=self.jid}) - :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}):up() - :tag('status', {code = '104'}):up(); + :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) + :tag('status', {code = '104'}):up() + :up(); if changed.whois then local code = (self:get_whois() == 'moderators') and "173" or "172"; msg.tags[1]:tag('status', {code = code}):up(); -- cgit v1.2.3 From 5d2b35e070ebe5d10aa07c5a5f8448aa17dfef44 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 18:33:57 -0400 Subject: plugins/muc/muc.lib: nick change unavailables should be from original occupant jid --- plugins/muc/muc.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 08ca0384..6453cc3b 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -760,7 +760,7 @@ function room_mt:handle_presence_to_occupant(origin, stanza) -- self:build_item_list(orig_occupant, x, false); -- COMPAT x:tag("status", {code = "303";}):up(); x:tag("status", {code = "110";}):up(); - self:route_stanza(st.presence{from = dest_occupant.nick, to = real_jid, type = "unavailable"}:add_child(x)); + self:route_stanza(st.presence{from = orig_occupant.nick, to = real_jid, type = "unavailable"}:add_child(x)); end self:save_occupant(orig_occupant); self:publicise_occupant_status(orig_occupant, orig_x); -- cgit v1.2.3 From 7cbdc2b0f7570ca06ff5e8561b09cb5bc64c8a1f Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 18:47:35 -0400 Subject: plugins/muc/muc.lib: Fix typo (moderators vs moderator) --- plugins/muc/muc.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 6453cc3b..c1747440 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -261,7 +261,7 @@ function room_mt:publicise_occupant_status(occupant, full_x, actor, reason) for nick, n_occupant in self:each_occupant() do if nick ~= occupant.nick or n_occupant.role == nil then local pr = full_p; - if has_anonymous and n_occupant.role ~= "moderators" and occupant.bare_jid ~= n_occupant.bare_jid then + if has_anonymous and n_occupant.role ~= "moderator" and occupant.bare_jid ~= n_occupant.bare_jid then pr = anon_p; end self:route_to_occupant(n_occupant, pr); -- cgit v1.2.3 From 672d0fae0b9b785bc2a1def3143b4978fd379201 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 20:25:10 -0400 Subject: plugins/muc/muc.lib: Subjects get sent even if empty. --- plugins/muc/muc.lib.lua | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index c1747440..7704a099 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -424,26 +424,21 @@ end function room_mt:get_subject() return self._data['subject'], self._data['subject_from'] end -local function create_subject_message(subject) - return st.message({type='groupchat'}) +local function create_subject_message(from, subject) + return st.message({from = from; type = "groupchat"}) :tag('subject'):text(subject):up(); end function room_mt:send_subject(to) - local from, subject = self:get_subject() - if subject then - local msg = create_subject_message(subject) - msg.attr.from = from - msg.attr.to = to - self:route_stanza(msg); - end + local msg = create_subject_message(self:get_subject()); + msg.attr.to = to; + self:route_stanza(msg); end function room_mt:set_subject(current_nick, subject) if subject == "" then subject = nil; end self._data['subject'] = subject; self._data['subject_from'] = current_nick; if self.save then self:save(); end - local msg = create_subject_message(subject) - msg.attr.from = current_nick + local msg = create_subject_message(current_nick, subject); self:broadcast_message(msg, false); return true; end -- cgit v1.2.3 From d0e5de9af2239bd57d91a7c30e0a4d335b668a7b Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 28 Mar 2014 20:28:20 -0400 Subject: plugins/muc/muc.lib: Use occupant methods where possible --- plugins/muc/muc.lib.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 7704a099..684539bd 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -641,7 +641,7 @@ module:hook("muc-occupant-joined", function(event) local real_jid = stanza.attr.from; room:send_occupant_list(real_jid, function(nick, occupant) -- Don't include self - return occupant.sessions[real_jid] == nil + return occupant:get_presence(real_jid) == nil; end); room:send_history(stanza); room:send_subject(real_jid); @@ -687,7 +687,8 @@ function room_mt:handle_presence_to_occupant(origin, stanza) local is_last_orig_session; if orig_occupant ~= nil then -- Is there are least 2 sessions? - is_last_orig_session = next(orig_occupant.sessions, next(orig_occupant.sessions)) == nil; + local iter, ob, last = orig_occupant:each_session(); + is_last_orig_session = iter(ob, iter(ob, last)) == nil; end local event, event_name = { -- cgit v1.2.3 From eb0739676fb5a91c11583764f1198999893f94ba Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 31 Mar 2014 12:21:43 -0400 Subject: plugins/muc/muc.lib: Remove top level pre-join event. Assign event priorities for other handlers --- plugins/muc/muc.lib.lua | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 684539bd..9f7ba8d3 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -585,12 +585,6 @@ module:hook("muc-room-pre-create", function(event) end, -1); module:hook("muc-occupant-pre-join", function(event) - return module:fire_event("muc-occupant-pre-join/affiliation", event) - or module:fire_event("muc-occupant-pre-join/password", event) - or module:fire_event("muc-occupant-pre-join/locked", event); -end, -1) - -module:hook("muc-occupant-pre-join/password", function(event) local room, stanza = event.room, event.stanza; local password = stanza:get_child("x", "http://jabber.org/protocol/muc"); password = password and password:get_child_text("password", "http://jabber.org/protocol/muc"); @@ -603,17 +597,17 @@ module:hook("muc-occupant-pre-join/password", function(event) event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; end -end, -1); +end, -20); -module:hook("muc-occupant-pre-join/locked", function(event) +module:hook("muc-occupant-pre-join", function(event) if event.room:is_locked() then -- Deny entry event.origin.send(st.error_reply(event.stanza, "cancel", "item-not-found")); return true; end -end, -1); +end, -30); -- registration required for entering members-only room -module:hook("muc-occupant-pre-join/affiliation", function(event) +module:hook("muc-occupant-pre-join", function(event) local room, stanza = event.room, event.stanza; local affiliation = room:get_affiliation(stanza.attr.from); if affiliation == nil and event.room:get_members_only() then @@ -622,10 +616,10 @@ module:hook("muc-occupant-pre-join/affiliation", function(event) event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; end -end, -1); +end, -5); -- check if user is banned -module:hook("muc-occupant-pre-join/affiliation", function(event) +module:hook("muc-occupant-pre-join", function(event) local room, stanza = event.room, event.stanza; local affiliation = room:get_affiliation(stanza.attr.from); if affiliation == "outcast" then @@ -634,7 +628,7 @@ module:hook("muc-occupant-pre-join/affiliation", function(event) event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); return true; end -end, -1); +end, -10); module:hook("muc-occupant-joined", function(event) local room, stanza = event.room, event.stanza; -- cgit v1.2.3 From 0cfac6e454973e4d7bb2f15e5f775adb43276ad4 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 31 Mar 2014 12:31:15 -0400 Subject: plugins/muc/muc.lib: Tidy up muc-invite event. - Send inside of the actual handle_invite function - Move password, compat and body tagging into event handlers --- plugins/muc/muc.lib.lua | 64 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 20 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 9f7ba8d3..93f8fc96 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1203,27 +1203,15 @@ function room_mt:handle_mediated_invite(origin, stanza) end local _invitee = jid_prep(payload.attr.to); if _invitee then - local _reason = payload:get_child_text("reason"); - if self:get_whois() == "moderators" then - _from = current_nick; - end local invite = st.message({from = _to, to = _invitee, id = stanza.attr.id}) :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) :tag('invite', {from=_from}) - :tag('reason'):text(_reason or ""):up() - :up(); - local password = self:get_password(); - if password then - invite:tag("password"):text(password):up(); - end - invite:up() - :tag('x', {xmlns="jabber:x:conference", jid=_to}) -- COMPAT: Some older clients expect this - :text(_reason or "") - :up() - :tag('body') -- Add a plain message for clients which don't support invites - :text(_from..' invited you to the room '.._to..(_reason and (' ('.._reason..')') or "")) + :tag('reason'):text(payload:get_child_text("reason")):up() + :up() :up(); - module:fire_event("muc-invite", {room = self, stanza = invite, origin = origin, incoming = stanza}); + if not module:fire_event("muc-invite", {room = self, stanza = invite, origin = origin, incoming = stanza}) then + self:route_stanza(invite); + end return true; else origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); @@ -1231,10 +1219,46 @@ function room_mt:handle_mediated_invite(origin, stanza) end end +-- Add password to outgoing invite module:hook("muc-invite", function(event) - event.room:route_stanza(event.stanza); - return true; -end, -1) + local password = event.room:get_password(); + if password then + local x = event.stanza:get_child("x", "http://jabber.org/protocol/muc#user"); + x:tag("password"):text(password):up(); + end +end); + +-- COMPAT: Some older clients expect this +module:hook("muc-invite", function(event) + local room, stanza = event.room, event.stanza; + local invite = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); + local reason = invite:get_child_text("reason"); + stanza:tag('x', {xmlns = "jabber:x:conference"; jid = room.jid;}) + :text(reason or "") + :up(); +end); + +-- Add a plain message for clients which don't support invites +module:hook("muc-invite", function(event) + local room, stanza = event.room, event.stanza; + local invite = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); + local reason = invite:get_child_text("reason") or ""; + stanza:tag("body") + :text(invite.attr.from.." invited you to the room "..room.jid..(reason == "" and (" ("..reason..")") or "")) + :up(); +end); + +-- Mask 'from' jid as occupant jid if room is anonymous +module:hook("muc-invite", function(event) + local room, stanza = event.room, event.stanza; + if room:get_whois() == "moderators" and room:get_default_role(room:get_affiliation(stanza.attr.to)) ~= "moderator" then + local invite = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); + local occupant_jid = room:get_occupant_jid(invite.attr.from); + if occupant_jid ~= nil then -- FIXME: This will expose real jid if inviter is not in room + invite.attr.from = occupant_jid; + end + end +end, 50); -- When an invite is sent; add an affiliation for the invitee module:hook("muc-invite", function(event) -- cgit v1.2.3 From b3f385662f6c306d09b4409d3eef61bc91f57f7b Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 31 Mar 2014 13:54:27 -0400 Subject: plugins/muc/muc.lib: Add pre-invite event. Move role check to it --- plugins/muc/muc.lib.lua | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 93f8fc96..a5118430 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1192,31 +1192,37 @@ function room_mt:handle_presence_to_room(origin, stanza) return handled; end -function room_mt:handle_mediated_invite(origin, stanza) - local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite") +-- Need visitor role or higher to invite +module:hook("muc-pre-invite", function(event) + local room, stanza = event.room, event.stanza; local _from, _to = stanza.attr.from, stanza.attr.to; - local current_nick = self:get_occupant_jid(_from) - -- Need visitor role or higher to invite - if not self:get_role(current_nick) or not self:get_default_role(self:get_affiliation(_from)) then - origin.send(st.error_reply(stanza, "auth", "forbidden")); + local inviter = room:get_occupant_by_real_jid(_from); + local role = inviter and inviter.role or room:get_default_role(room:get_affiliation(_from)); + if valid_roles[role or "none"] <= valid_roles.visitor then + event.origin.send(st.error_reply(stanza, "auth", "forbidden")); return true; end - local _invitee = jid_prep(payload.attr.to); - if _invitee then - local invite = st.message({from = _to, to = _invitee, id = stanza.attr.id}) - :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) - :tag('invite', {from=_from}) - :tag('reason'):text(payload:get_child_text("reason")):up() - :up() - :up(); - if not module:fire_event("muc-invite", {room = self, stanza = invite, origin = origin, incoming = stanza}) then - self:route_stanza(invite); - end - return true; - else +end); + +function room_mt:handle_mediated_invite(origin, stanza) + local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); + local invitee = jid_prep(payload.attr.to); + if not invitee then origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); return true; + elseif not module:fire_event("muc-pre-invite", {room = self, origin = origin, stanza = stanza}) then + return true; + end + local invite = st.message({from = self.jid, to = invitee, id = stanza.attr.id}) + :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) + :tag('invite', {from = stanza.attr.from;}) + :tag('reason'):text(payload:get_child_text("reason")):up() + :up() + :up(); + if not module:fire_event("muc-invite", {room = self, stanza = invite, origin = origin, incoming = stanza}) then + self:route_stanza(invite); end + return true; end -- Add password to outgoing invite -- cgit v1.2.3 From 8b8f89703e632711ee0a6371a2f7e2400176b692 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 31 Mar 2014 14:06:35 -0400 Subject: plugins/muc/muc.lib: Update declines to be more like invites --- plugins/muc/muc.lib.lua | 53 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index a5118430..58fa7b83 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1279,39 +1279,40 @@ module:hook("muc-invite", function(event) end); function room_mt:handle_mediated_decline(origin, stanza) - local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("decline") + local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("decline"); local declinee = jid_prep(payload.attr.to); - if declinee then - local from, to = stanza.attr.from, stanza.attr.to; - -- TODO: Validate declinee - local reason = payload:get_child_text("reason") - local decline = st.message({from = to, to = declinee, id = stanza.attr.id}) - :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) - :tag('decline', {from=from}) - :tag('reason'):text(reason or ""):up() - :up() - :up() - :tag('body') -- Add a plain message for clients which don't support declines - :text(from..' declined your invite to the room '..to..(reason and (' ('..reason..')') or "")) - :up(); - module:fire_event("muc-decline", { room = self, stanza = decline, origin = origin, incoming = stanza }); - return true; - else + if not declinee then origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); return true; + elseif not module:fire_event("muc-pre-decline", {room = self, origin = origin, stanza = stanza}) then + return true; end + local decline = st.message({from = self.jid, to = declinee, id = stanza.attr.id}) + :tag("x", {xmlns = "http://jabber.org/protocol/muc#user"}) + :tag("decline", {from = stanza.attr.from}) + :tag("reason"):text(payload:get_child_text("reason")):up() + :up() + :up(); + if not module:fire_event("muc-decline", {room = self, stanza = decline, origin = origin, incoming = stanza}) then + local occupant = self:get_occupant_by_real_jid(decline.attr.to); + if occupant then + self:route_to_occupant(occupant, decline); + else + self:route_stanza(decline); + end + end + return true; end +-- Add a plain message for clients which don't support declines module:hook("muc-decline", function(event) - local room, stanza = event.room, event.stanza - local occupant = room:get_occupant_by_real_jid(stanza.attr.to); - if occupant then - room:route_to_occupant(occupant, stanza) - else - room:route_stanza(stanza); - end - return true; -end, -1) + local room, stanza = event.room, event.stanza; + local decline = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("decline"); + local reason = decline:get_child_text("reason") or ""; + stanza:tag("body") + :text(decline.attr.from.." declined your invite to the room "..room.jid..(reason == "" and (" ("..reason..")") or "")) + :up(); +end); function room_mt:handle_message_to_room(origin, stanza) local type = stanza.attr.type; -- cgit v1.2.3 From e19853af0e7f6049fd43e3d061f4a1f54cbb8fa6 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 31 Mar 2014 14:44:52 -0400 Subject: plugins/muc/muc.lib: Don't try and get occupant jids for annotating invite affiliation changes --- plugins/muc/muc.lib.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 58fa7b83..d6ba68f9 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1272,9 +1272,8 @@ module:hook("muc-invite", function(event) local invitee = stanza.attr.to if room:get_members_only() and not room:get_affiliation(invitee) then local from = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite").attr.from - local current_nick = room:get_occupant_jid(from) log("debug", "%s invited %s into members only room %s, granting membership", from, invitee, room.jid); - room:set_affiliation(from, invitee, "member", "Invited by " .. current_nick) + room:set_affiliation(from, invitee, "member", "Invited by " .. from); -- This might fail; ignore for now end end); -- cgit v1.2.3 From 2c3dad041615c903fd05174183b7d2703681cc78 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 31 Mar 2014 14:45:42 -0400 Subject: plugins/muc/muc.lib: restrict invitations in members only rooms to admins --- plugins/muc/muc.lib.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index d6ba68f9..c8881178 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1204,6 +1204,17 @@ module:hook("muc-pre-invite", function(event) end end); +-- Invitation privileges in members-only rooms SHOULD be restricted to room admins; +-- if a member without privileges to edit the member list attempts to invite another user +-- the service SHOULD return a error to the occupant +module:hook("muc-pre-invite", function(event) + local room, stanza = event.room, event.stanza; + if room:get_members_only() and valid_affiliations[room:get_affiliation(stanza.attr.from) or "none"] < valid_affiliations.admin then + event.origin.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + end +end); + function room_mt:handle_mediated_invite(origin, stanza) local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); local invitee = jid_prep(payload.attr.to); -- cgit v1.2.3 From fbae5113588b6dd2743d72c29290613c3b7f9812 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 31 Mar 2014 14:46:59 -0400 Subject: plugins/muc/muc.lib: Add muc-room-locked event --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index c8881178..d839786a 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -88,6 +88,7 @@ function room_mt:get_default_role(affiliation) end function room_mt:lock() + module:fire_event("muc-room-locked", { room = self }); self.locked = true end function room_mt:unlock() -- cgit v1.2.3 From b06bdb9b7d3da8b1c30ae09a8279396acbb8cad9 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 1 Apr 2014 15:41:44 -0400 Subject: plugins/muc/muc.lib: Split up get_disco_info into events This was done so we can split off functionality to other files later (e.g. plugins/muc/password) --- plugins/muc/muc.lib.lua | 55 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 16 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index d839786a..5f504d40 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -397,23 +397,46 @@ function room_mt:send_history(stanza) end function room_mt:get_disco_info(stanza) - local count = 0; for _ in self:each_occupant() do count = count + 1; end - return st.reply(stanza):query("http://jabber.org/protocol/disco#info") - :tag("identity", {category="conference", type="text", name=self:get_name()}):up() - :tag("feature", {var="http://jabber.org/protocol/muc"}):up() - :tag("feature", {var=self:get_password() and "muc_passwordprotected" or "muc_unsecured"}):up() - :tag("feature", {var=self:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up() - :tag("feature", {var=self:get_members_only() and "muc_membersonly" or "muc_open"}):up() - :tag("feature", {var=self:get_persistent() and "muc_persistent" or "muc_temporary"}):up() - :tag("feature", {var=self:get_hidden() and "muc_hidden" or "muc_public"}):up() - :tag("feature", {var=self:get_whois() ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up() - :add_child(dataform.new({ - { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" }, - { name = "muc#roominfo_description", label = "Description", value = "" }, - { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) } - }):form({["muc#roominfo_description"] = self:get_description()}, 'result')) - ; + local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#info"); + local form = dataform.new { + { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" }; + }; + module:fire_event("muc-disco#info", {room = self; reply = reply; form = form;}); + reply:add_child(form:form(nil, "result")); + return reply; end +module:hook("muc-disco#info", function(event) + event.reply:tag("identity", {category="conference", type="text", name=event.room:get_name()}):up(); +end); +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = "http://jabber.org/protocol/muc"}):up(); +end); +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = event.room:get_password() and "muc_passwordprotected" or "muc_unsecured"}):up(); +end); +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = event.room:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up(); +end); +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = event.room:get_members_only() and "muc_membersonly" or "muc_open"}):up(); +end); +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = event.room:get_persistent() and "muc_persistent" or "muc_temporary"}):up(); +end); +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = event.room:get_hidden() and "muc_hidden" or "muc_public"}):up(); +end); +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = event.room:get_whois() ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up(); +end); +module:hook("muc-disco#info", function(event) + table.insert(event.form, { name = "muc#roominfo_description", label = "Description", value = event.room:get_description() }); +end); +module:hook("muc-disco#info", function(event) + local count = 0; for _ in event.room:each_occupant() do count = count + 1; end + table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }); +end); + function room_mt:get_disco_items(stanza) local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#items"); for room_jid in self:each_occupant() do -- cgit v1.2.3 From 65b712f91dbc129a53575e974e3bc1463fedca92 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 1 Apr 2014 17:10:01 -0400 Subject: plugins/muc/muc.lib: Move default config layout into hooks --- plugins/muc/muc.lib.lua | 148 +++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 64 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 5f504d40..df31a83c 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -907,7 +907,6 @@ function room_mt:send_form(origin, stanza) end function room_mt:get_form_layout(actor) - local whois = self:get_whois() local form = dataform.new({ title = "Configuration for "..self.jid, instructions = "Complete and submit this form to configure the room.", @@ -915,73 +914,94 @@ function room_mt:get_form_layout(actor) name = 'FORM_TYPE', type = 'hidden', value = 'http://jabber.org/protocol/muc#roomconfig' - }, - { - name = 'muc#roomconfig_roomname', - type = 'text-single', - label = 'Name', - value = self:get_name() or "", - }, - { - name = 'muc#roomconfig_roomdesc', - type = 'text-single', - label = 'Description', - value = self:get_description() or "", - }, - { - name = 'muc#roomconfig_persistentroom', - type = 'boolean', - label = 'Make Room Persistent?', - value = self:get_persistent() - }, - { - name = 'muc#roomconfig_publicroom', - type = 'boolean', - label = 'Make Room Publicly Searchable?', - value = not self:get_hidden() - }, - { - name = 'muc#roomconfig_changesubject', - type = 'boolean', - label = 'Allow Occupants to Change Subject?', - value = self:get_changesubject() - }, - { - name = 'muc#roomconfig_whois', - type = 'list-single', - label = 'Who May Discover Real JIDs?', - value = { - { value = 'moderators', label = 'Moderators Only', default = whois == 'moderators' }, - { value = 'anyone', label = 'Anyone', default = whois == 'anyone' } - } - }, - { - name = 'muc#roomconfig_roomsecret', - type = 'text-private', - label = 'Password', - value = self:get_password() or "", - }, - { - name = 'muc#roomconfig_moderatedroom', - type = 'boolean', - label = 'Make Room Moderated?', - value = self:get_moderated() - }, - { - name = 'muc#roomconfig_membersonly', - type = 'boolean', - label = 'Make Room Members-Only?', - value = self:get_members_only() - }, - { - name = 'muc#roomconfig_historylength', - type = 'text-single', - label = 'Maximum Number of History Messages Returned by Room', - value = tostring(self:get_historylength()) } }); return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form; end +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_roomname', + type = 'text-single', + label = 'Name', + value = event.room:get_name() or "", + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_roomdesc', + type = 'text-single', + label = 'Description', + value = event.room:get_description() or "", + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_persistentroom', + type = 'boolean', + label = 'Make Room Persistent?', + value = event.room:get_persistent() + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_publicroom', + type = 'boolean', + label = 'Make Room Publicly Searchable?', + value = not event.room:get_hidden() + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_changesubject', + type = 'boolean', + label = 'Allow Occupants to Change Subject?', + value = event.room:get_changesubject() + }); +end); +module:hook("muc-config-form", function(event) + local whois = event.room:get_whois(); + table.insert(event.form, { + name = 'muc#roomconfig_whois', + type = 'list-single', + label = 'Who May Discover Real JIDs?', + value = { + { value = 'moderators', label = 'Moderators Only', default = whois == 'moderators' }, + { value = 'anyone', label = 'Anyone', default = whois == 'anyone' } + } + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_roomsecret', + type = 'text-private', + label = 'Password', + value = event.room:get_password() or "", + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_moderatedroom', + type = 'boolean', + label = 'Make Room Moderated?', + value = event.room:get_moderated() + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_membersonly', + type = 'boolean', + label = 'Make Room Members-Only?', + value = event.room:get_members_only() + }); +end); +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = 'muc#roomconfig_historylength', + type = 'text-single', + label = 'Maximum Number of History Messages Returned by Room', + value = tostring(event.room:get_historylength()) + }); +end); function room_mt:process_form(origin, stanza) local query = stanza.tags[1]; -- cgit v1.2.3 From 5f51a5c88afc3989c2aa70ff00ea07c854dfcd61 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 1 Apr 2014 17:45:03 -0400 Subject: plugins/muc/muc.lib: Refactor out process_form into hooks --- plugins/muc/muc.lib.lua | 117 +++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 46 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index df31a83c..7fd9746c 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1004,59 +1004,82 @@ module:hook("muc-config-form", function(event) end); function room_mt:process_form(origin, stanza) - local query = stanza.tags[1]; - local form = query:get_child("x", "jabber:x:data") - if not form then origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); return; end - if form.attr.type == "cancel" then origin.send(st.reply(stanza)); return; end - if form.attr.type ~= "submit" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); return; end - - local fields = self:get_form_layout(stanza.attr.from):data(form); - if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Form is not of type room configuration")); return; end - - - local changed = {}; + local form = stanza.tags[1]:get_child("x", "jabber:x:data"); + if form.attr.type == "cancel" then + origin.send(st.reply(stanza)); + elseif form.attr.type == "submit" then + local fields = self:get_form_layout(stanza.attr.from):data(form); + if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then + origin.send(st.error_reply(stanza, "cancel", "bad-request", "Form is not of type room configuration")); + return true; + end - local function handle_option(name, field, allowed) - local new = fields[field]; - if new == nil then return; end - if allowed and not allowed[new] then return; end - if new == self["get_"..name](self) then return; end - changed[name] = true; - self["set_"..name](self, new); - end + local changed = {}; - local event = { room = self, fields = fields, changed = changed, stanza = stanza, origin = origin, update_option = handle_option }; - module:fire_event("muc-config-submitted", event); + local function handle_option(name, field, allowed) + local new = fields[field]; + if new == nil then return; end + if allowed and not allowed[new] then return; end + if new == self["get_"..name](self) then return; end + changed[name] = true; + self["set_"..name](self, new); + end - handle_option("name", "muc#roomconfig_roomname"); - handle_option("description", "muc#roomconfig_roomdesc"); - handle_option("persistent", "muc#roomconfig_persistentroom"); - handle_option("moderated", "muc#roomconfig_moderatedroom"); - handle_option("members_only", "muc#roomconfig_membersonly"); - handle_option("public", "muc#roomconfig_publicroom"); - handle_option("changesubject", "muc#roomconfig_changesubject"); - handle_option("historylength", "muc#roomconfig_historylength"); - handle_option("whois", "muc#roomconfig_whois", valid_whois); - handle_option("password", "muc#roomconfig_roomsecret"); + local event = { room = self, fields = fields, changed = changed, stanza = stanza, origin = origin, update_option = handle_option }; + module:fire_event("muc-config-submitted", event); - if self.save then self:save(true); end - if self:is_locked() then - self:unlock(); - end - origin.send(st.reply(stanza)); + if self.save then self:save(true); end + if self:is_locked() then + self:unlock(); + end + origin.send(st.reply(stanza)); - if next(changed) then - local msg = st.message({type='groupchat', from=self.jid}) - :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) - :tag('status', {code = '104'}):up() - :up(); - if changed.whois then - local code = (self:get_whois() == 'moderators') and "173" or "172"; - msg.tags[1]:tag('status', {code = code}):up(); + if next(changed) then + local msg = st.message({type='groupchat', from=self.jid}) + :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) + :tag('status', {code = '104'}):up() + :up(); + if changed.whois then + local code = (self:get_whois() == 'moderators') and "173" or "172"; + msg.tags[1]:tag('status', {code = code}):up(); + end + self:broadcast_message(msg, false) end - self:broadcast_message(msg, false) + else + origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); end + return true; end +module:hook("muc-config-submitted", function(event) + event.update_option("name", "muc#roomconfig_roomname"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("description", "muc#roomconfig_roomdesc"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("persistent", "muc#roomconfig_persistentroom"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("moderated", "muc#roomconfig_moderatedroom"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("members_only", "muc#roomconfig_membersonly"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("public", "muc#roomconfig_publicroom"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("changesubject", "muc#roomconfig_changesubject"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("historylength", "muc#roomconfig_historylength"); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("whois", "muc#roomconfig_whois", valid_whois); +end); +module:hook("muc-config-submitted", function(event) + event.update_option("password", "muc#roomconfig_roomsecret"); +end); -- Removes everyone from the room function room_mt:clear(x) @@ -1188,8 +1211,10 @@ function room_mt:handle_owner_query_set_to_room(origin, stanza) self:destroy(newjid, reason, password); origin.send(st.reply(stanza)); return true; + elseif child.name == "x" and child.attr.xmlns == "jabber:x:data" then + return self:process_form(origin, stanza); else - self:process_form(origin, stanza); + origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); return true; end end -- cgit v1.2.3 From 1782c1a4b939afc68f4ccee448726f61dfc58c44 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 11:35:00 -0400 Subject: plugins/muc/muc.lib: Modify muc-config-submitted to keep a list of status codes instead of fields changed --- plugins/muc/muc.lib.lua | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 7fd9746c..07069130 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1014,18 +1014,16 @@ function room_mt:process_form(origin, stanza) return true; end - local changed = {}; - - local function handle_option(name, field, allowed) + local event = {room = self; origin = origin; stanza = stanza; fields = fields; status_codes = {};}; + function event.update_option(name, field, allowed) local new = fields[field]; if new == nil then return; end if allowed and not allowed[new] then return; end if new == self["get_"..name](self) then return; end - changed[name] = true; + event.status_codes["104"] = true; self["set_"..name](self, new); + return true; end - - local event = { room = self, fields = fields, changed = changed, stanza = stanza, origin = origin, update_option = handle_option }; module:fire_event("muc-config-submitted", event); if self.save then self:save(true); end @@ -1034,15 +1032,13 @@ function room_mt:process_form(origin, stanza) end origin.send(st.reply(stanza)); - if next(changed) then + if next(event.status_codes) then local msg = st.message({type='groupchat', from=self.jid}) :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) - :tag('status', {code = '104'}):up() - :up(); - if changed.whois then - local code = (self:get_whois() == 'moderators') and "173" or "172"; - msg.tags[1]:tag('status', {code = code}):up(); + for code in pairs(event.status_codes) do + msg:tag("status", {code = code;}):up(); end + msg:up(); self:broadcast_message(msg, false) end else @@ -1075,7 +1071,10 @@ module:hook("muc-config-submitted", function(event) event.update_option("historylength", "muc#roomconfig_historylength"); end); module:hook("muc-config-submitted", function(event) - event.update_option("whois", "muc#roomconfig_whois", valid_whois); + if event.update_option("whois", "muc#roomconfig_whois", valid_whois) then + local code = (event.room:get_whois() == 'moderators') and "173" or "172"; + event.status_codes[code] = true; + end end); module:hook("muc-config-submitted", function(event) event.update_option("password", "muc#roomconfig_roomsecret"); -- cgit v1.2.3 From 906e49003903ef31a24eba95b20f9d4b1fc21431 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 15:14:52 -0400 Subject: plugins/muc/muc.lib: Move description functions out to own file --- plugins/muc/description.lib.lua | 43 +++++++++++++++++++++++++++++++++++++++++ plugins/muc/muc.lib.lua | 28 ++++----------------------- 2 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 plugins/muc/description.lib.lua (limited to 'plugins') diff --git a/plugins/muc/description.lib.lua b/plugins/muc/description.lib.lua new file mode 100644 index 00000000..30852922 --- /dev/null +++ b/plugins/muc/description.lib.lua @@ -0,0 +1,43 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local function get_description(room) + return room._data.description; +end + +local function set_description(room, description) + if description == "" then description = nil; end + if get_description(room) == description then return false; end + room._data.description = description; + if room.save then room:save(true); end + return true; +end + +local function add_form_option(event) + table.insert(event.form, { + name = "muc#roomconfig_roomdesc"; + type = "text-single"; + label = "Description"; + value = get_description(event.room) or ""; + }); +end +module:hook("muc-disco#info", add_form_option); +module:hook("muc-config-form", add_form_option); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_roomdesc"]; + if new ~= nil and set_description(event.room, new) then + event.status_codes["104"] = true; + end +end); + +return { + get = get_description; + set = set_description; +}; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 07069130..ef572cfd 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -429,9 +429,6 @@ end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_whois() ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up(); end); -module:hook("muc-disco#info", function(event) - table.insert(event.form, { name = "muc#roominfo_description", label = "Description", value = event.room:get_description() }); -end); module:hook("muc-disco#info", function(event) local count = 0; for _ in event.room:each_occupant() do count = count + 1; end table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }); @@ -495,16 +492,6 @@ end function room_mt:get_name() return self._data.name or jid_split(self.jid); end -function room_mt:set_description(description) - if description == "" or type(description) ~= "string" then description = nil; end - if self._data.description ~= description then - self._data.description = description; - if self.save then self:save(true); end - end -end -function room_mt:get_description() - return self._data.description; -end function room_mt:set_password(password) if password == "" or type(password) ~= "string" then password = nil; end if self._data.password ~= password then @@ -926,14 +913,6 @@ module:hook("muc-config-form", function(event) value = event.room:get_name() or "", }); end); -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_roomdesc', - type = 'text-single', - label = 'Description', - value = event.room:get_description() or "", - }); -end); module:hook("muc-config-form", function(event) table.insert(event.form, { name = 'muc#roomconfig_persistentroom', @@ -1049,9 +1028,6 @@ end module:hook("muc-config-submitted", function(event) event.update_option("name", "muc#roomconfig_roomname"); end); -module:hook("muc-config-submitted", function(event) - event.update_option("description", "muc#roomconfig_roomdesc"); -end); module:hook("muc-config-submitted", function(event) event.update_option("persistent", "muc#roomconfig_persistentroom"); end); @@ -1558,6 +1534,10 @@ function room_mt:set_role(actor, occupant_jid, role, reason) return true; end +local description = module:require "muc/description"; +room_mt.get_description = description.get; +room_mt.set_description = description.set; + local _M = {}; -- module "muc" function _M.new_room(jid, config) -- cgit v1.2.3 From 7da7cb7b4a7125eb1d77e67bb393ceab50355a07 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 15:36:08 -0400 Subject: plugins/muc/mod_muc: Move room locking into hook --- plugins/muc/mod_muc.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index a8a6388d..8b40d6ad 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -90,7 +90,13 @@ function create_room(jid) local room = muc_new_room(jid); room.save = room_save; rooms[jid] = room; - if lock_rooms then + module:fire_event("muc-room-created", { room = room }); + return room; +end + +if lock_rooms then + module:hook("muc-room-created", function(event) + local room = event.room; room:lock(); if lock_room_timeout and lock_room_timeout > 0 then module:add_timer(lock_room_timeout, function () @@ -99,9 +105,7 @@ function create_room(jid) end end); end - end - module:fire_event("muc-room-created", { room = room }); - return room; + end); end function forget_room(jid) -- cgit v1.2.3 From fd4362f97d1d75fa0ae251417801e9c82b4ef03c Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 15:48:25 -0400 Subject: plugins/muc: Move locking to seperate module --- plugins/muc/lock.lib.lua | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ plugins/muc/mod_muc.lua | 17 +------------ plugins/muc/muc.lib.lua | 30 ---------------------- 3 files changed, 66 insertions(+), 46 deletions(-) create mode 100644 plugins/muc/lock.lib.lua (limited to 'plugins') diff --git a/plugins/muc/lock.lib.lua b/plugins/muc/lock.lib.lua new file mode 100644 index 00000000..73dfa151 --- /dev/null +++ b/plugins/muc/lock.lib.lua @@ -0,0 +1,65 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local lock_rooms = module:get_option_boolean("muc_room_locking", false); +local lock_room_timeout = module:get_option_number("muc_room_lock_timeout", 300); + +local function lock(room) + module:fire_event("muc-room-locked", {room = room;}); + room.locked = true; +end +local function unlock(room) + module:fire_event("muc-room-unlocked", {room = room;}); + room.locked = nil; +end +local function is_locked(room) + return not not room.locked; +end + +if lock_rooms then + module:hook("muc-room-created", function(event) + local room = event.room; + lock(room); + if lock_room_timeout and lock_room_timeout > 0 then + module:add_timer(lock_room_timeout, function () + if is_locked(room) then + room:destroy(); -- Not unlocked in time + end + end); + end + end); +end + +-- Older groupchat protocol doesn't lock +module:hook("muc-room-pre-create", function(event) + if is_locked(event.room) and not event.stanza:get_child("x", "http://jabber.org/protocol/muc") then + unlock(event.room); + end +end, 10); + +-- Don't let users into room while it is locked +module:hook("muc-occupant-pre-join", function(event) + if is_locked(event.room) then -- Deny entry + event.origin.send(st.error_reply(event.stanza, "cancel", "item-not-found")); + return true; + end +end, -30); + +-- When config is submitted; unlock the room +module:hook("muc-config-submitted", function(event) + if is_locked(event.room) then + unlock(event.room); + end +end, -1); + +return { + lock = lock; + unlock = unlock; + is_locked = is_locked; +}; diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 8b40d6ad..6f6094b4 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -23,8 +23,6 @@ if restrict_room_creation then restrict_room_creation = nil; end end -local lock_rooms = module:get_option_boolean("muc_room_locking", false); -local lock_room_timeout = module:get_option_number("muc_room_lock_timeout", 300); local muclib = module:require "muc"; local muc_new_room = muclib.new_room; @@ -47,6 +45,7 @@ module:depends("disco"); module:add_identity("conference", "text", muc_name); module:add_feature("http://jabber.org/protocol/muc"); module:depends "muc_unique" +module:require "muc/lock"; local function is_admin(jid) return um_is_admin(jid, module.host); @@ -94,20 +93,6 @@ function create_room(jid) return room; end -if lock_rooms then - module:hook("muc-room-created", function(event) - local room = event.room; - room:lock(); - if lock_room_timeout and lock_room_timeout > 0 then - module:add_timer(lock_room_timeout, function () - if room:is_locked() then - room:destroy(); -- Not unlocked in time - end - end); - end - end); -end - function forget_room(jid) rooms[jid] = nil; end diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index ef572cfd..6906b9ce 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -87,18 +87,6 @@ function room_mt:get_default_role(affiliation) end end -function room_mt:lock() - module:fire_event("muc-room-locked", { room = self }); - self.locked = true -end -function room_mt:unlock() - module:fire_event("muc-room-unlocked", { room = self }); - self.locked = nil -end -function room_mt:is_locked() - return not not self.locked -end - --- Occupant functions function room_mt:new_occupant(bare_real_jid, nick) local occupant = occupant_lib.new(bare_real_jid, nick); @@ -583,13 +571,6 @@ function room_mt:get_whois() return self._data.whois; end -module:hook("muc-room-pre-create", function(event) - local room = event.room; - if room:is_locked() and not event.stanza:get_child("x", "http://jabber.org/protocol/muc") then - room:unlock(); -- Older groupchat protocol doesn't lock - end -end, 10); - -- Give the room creator owner affiliation module:hook("muc-room-pre-create", function(event) event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); @@ -610,13 +591,6 @@ module:hook("muc-occupant-pre-join", function(event) end end, -20); -module:hook("muc-occupant-pre-join", function(event) - if event.room:is_locked() then -- Deny entry - event.origin.send(st.error_reply(event.stanza, "cancel", "item-not-found")); - return true; - end -end, -30); - -- registration required for entering members-only room module:hook("muc-occupant-pre-join", function(event) local room, stanza = event.room, event.stanza; @@ -1006,9 +980,6 @@ function room_mt:process_form(origin, stanza) module:fire_event("muc-config-submitted", event); if self.save then self:save(true); end - if self:is_locked() then - self:unlock(); - end origin.send(st.reply(stanza)); if next(event.status_codes) then @@ -1543,7 +1514,6 @@ local _M = {}; -- module "muc" function _M.new_room(jid, config) return setmetatable({ jid = jid; - locked = nil; _jid_nick = {}; _occupants = {}; _data = { -- cgit v1.2.3 From 4c0e0c3c239a4ae8d19c78f0f4d6d6975fa7fdc8 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 15:56:37 -0400 Subject: plugins/muc/lock.lib: Need to let creator into the locked room :) --- plugins/muc/lock.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/muc/lock.lib.lua b/plugins/muc/lock.lib.lua index 73dfa151..7cf19be3 100644 --- a/plugins/muc/lock.lib.lua +++ b/plugins/muc/lock.lib.lua @@ -45,7 +45,7 @@ end, 10); -- Don't let users into room while it is locked module:hook("muc-occupant-pre-join", function(event) - if is_locked(event.room) then -- Deny entry + if not event.is_new_room and is_locked(event.room) then -- Deny entry event.origin.send(st.error_reply(event.stanza, "cancel", "item-not-found")); return true; end -- cgit v1.2.3 From 7f0b9f176bd26c13df53b938fc19c01d2aca51ee Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 16:57:59 -0400 Subject: plugins/muc: Move password functions to seperate module --- plugins/muc/muc.lib.lua | 52 +++----------------------------- plugins/muc/password.lib.lua | 70 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 plugins/muc/password.lib.lua (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 6906b9ce..62553321 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -399,9 +399,6 @@ end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = "http://jabber.org/protocol/muc"}):up(); end); -module:hook("muc-disco#info", function(event) - event.reply:tag("feature", {var = event.room:get_password() and "muc_passwordprotected" or "muc_unsecured"}):up(); -end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up(); end); @@ -480,16 +477,6 @@ end function room_mt:get_name() return self._data.name or jid_split(self.jid); end -function room_mt:set_password(password) - if password == "" or type(password) ~= "string" then password = nil; end - if self._data.password ~= password then - self._data.password = password; - if self.save then self:save(true); end - end -end -function room_mt:get_password() - return self._data.password; -end function room_mt:set_moderated(moderated) moderated = moderated and true or nil; if self._data.moderated ~= moderated then @@ -576,21 +563,6 @@ module:hook("muc-room-pre-create", function(event) event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); end, -1); -module:hook("muc-occupant-pre-join", function(event) - local room, stanza = event.room, event.stanza; - local password = stanza:get_child("x", "http://jabber.org/protocol/muc"); - password = password and password:get_child_text("password", "http://jabber.org/protocol/muc"); - if not password or password == "" then password = nil; end - if room:get_password() ~= password then - local from, to = stanza.attr.from, stanza.attr.to; - log("debug", "%s couldn't join due to invalid password: %s", from, to); - local reply = st.error_reply(stanza, "auth", "not-authorized"):up(); - reply.tags[1].attr.code = "401"; - event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); - return true; - end -end, -20); - -- registration required for entering members-only room module:hook("muc-occupant-pre-join", function(event) local room, stanza = event.room, event.stanza; @@ -923,14 +895,6 @@ module:hook("muc-config-form", function(event) } }); end); -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_roomsecret', - type = 'text-private', - label = 'Password', - value = event.room:get_password() or "", - }); -end); module:hook("muc-config-form", function(event) table.insert(event.form, { name = 'muc#roomconfig_moderatedroom', @@ -1023,9 +987,6 @@ module:hook("muc-config-submitted", function(event) event.status_codes[code] = true; end end); -module:hook("muc-config-submitted", function(event) - event.update_option("password", "muc#roomconfig_roomsecret"); -end); -- Removes everyone from the room function room_mt:clear(x) @@ -1251,15 +1212,6 @@ function room_mt:handle_mediated_invite(origin, stanza) return true; end --- Add password to outgoing invite -module:hook("muc-invite", function(event) - local password = event.room:get_password(); - if password then - local x = event.stanza:get_child("x", "http://jabber.org/protocol/muc#user"); - x:tag("password"):text(password):up(); - end -end); - -- COMPAT: Some older clients expect this module:hook("muc-invite", function(event) local room, stanza = event.room, event.stanza; @@ -1509,6 +1461,10 @@ local description = module:require "muc/description"; room_mt.get_description = description.get; room_mt.set_description = description.set; +local password = module:require "muc/password"; +room_mt.get_password = password.get; +room_mt.set_password = password.set; + local _M = {}; -- module "muc" function _M.new_room(jid, config) diff --git a/plugins/muc/password.lib.lua b/plugins/muc/password.lib.lua new file mode 100644 index 00000000..d169790a --- /dev/null +++ b/plugins/muc/password.lib.lua @@ -0,0 +1,70 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local function get_password(room) + return room._data.password; +end + +local function set_password(room, password) + if password == "" then password = nil; end + if room._data.password == password then return false; end + room._data.password = password; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = get_password(event.room) and "muc_passwordprotected" or "muc_unsecured"}):up(); +end); + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_roomsecret"; + type = "text-private"; + label = "Password"; + value = get_password(event.room) or ""; + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_roomsecret"]; + if new ~= nil and set_password(event.room, new) then + event.status_codes["104"] = true; + end +end); + +-- Don't allow anyone to join room unless they provide the password +module:hook("muc-occupant-pre-join", function(event) + local room, stanza = event.room, event.stanza; + local password = stanza:get_child("x", "http://jabber.org/protocol/muc"); + password = password and password:get_child_text("password", "http://jabber.org/protocol/muc"); + if not password or password == "" then password = nil; end + if get_password(room) ~= password then + local from, to = stanza.attr.from, stanza.attr.to; + module:log("debug", "%s couldn't join due to invalid password: %s", from, to); + local reply = st.error_reply(stanza, "auth", "not-authorized"):up(); + reply.tags[1].attr.code = "401"; + event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); + return true; + end +end, -20); + +-- Add password to outgoing invite +module:hook("muc-invite", function(event) + local password = get_password(event.room); + if password then + local x = event.stanza:get_child("x", "http://jabber.org/protocol/muc#user"); + x:tag("password"):text(password):up(); + end +end); + +return { + get = get_password; + set = set_password; +}; -- cgit v1.2.3 From 538ed6fc20ccd0b2622bc8b1b474c76db0cf8397 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 17:02:07 -0400 Subject: plugins/muc: Move name functions to seperate module --- plugins/muc/muc.lib.lua | 28 ++++------------------------ plugins/muc/name.lib.lua | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 plugins/muc/name.lib.lua (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 62553321..15e3f9c9 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -393,9 +393,6 @@ function room_mt:get_disco_info(stanza) reply:add_child(form:form(nil, "result")); return reply; end -module:hook("muc-disco#info", function(event) - event.reply:tag("identity", {category="conference", type="text", name=event.room:get_name()}):up(); -end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = "http://jabber.org/protocol/muc"}):up(); end); @@ -467,16 +464,6 @@ function room_mt:handle_kickable(origin, stanza) return true; end -function room_mt:set_name(name) - if name == "" or type(name) ~= "string" or name == (jid_split(self.jid)) then name = nil; end - if self._data.name ~= name then - self._data.name = name; - if self.save then self:save(true); end - end -end -function room_mt:get_name() - return self._data.name or jid_split(self.jid); -end function room_mt:set_moderated(moderated) moderated = moderated and true or nil; if self._data.moderated ~= moderated then @@ -851,14 +838,6 @@ function room_mt:get_form_layout(actor) }); return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form; end -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_roomname', - type = 'text-single', - label = 'Name', - value = event.room:get_name() or "", - }); -end); module:hook("muc-config-form", function(event) table.insert(event.form, { name = 'muc#roomconfig_persistentroom', @@ -960,9 +939,6 @@ function room_mt:process_form(origin, stanza) end return true; end -module:hook("muc-config-submitted", function(event) - event.update_option("name", "muc#roomconfig_roomname"); -end); module:hook("muc-config-submitted", function(event) event.update_option("persistent", "muc#roomconfig_persistentroom"); end); @@ -1457,6 +1433,10 @@ function room_mt:set_role(actor, occupant_jid, role, reason) return true; end +local name = module:require "muc/name"; +room_mt.get_name = name.get; +room_mt.set_name = name.set; + local description = module:require "muc/description"; room_mt.get_description = description.get; room_mt.set_description = description.set; diff --git a/plugins/muc/name.lib.lua b/plugins/muc/name.lib.lua new file mode 100644 index 00000000..49d12467 --- /dev/null +++ b/plugins/muc/name.lib.lua @@ -0,0 +1,47 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local jid_split = require "util.jid".split; + +local function get_name(room) + return room._data.name or jid_split(room.jid); +end + +local function set_name(room, name) + if name == "" or name == (jid_split(room.jid)) then name = nil; end + if room._data.name == name then return false; end + room._data.name = name; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-disco#info", function(event) + event.reply:tag("identity", {category="conference", type="text", name=get_name(event.room)}):up(); +end); + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_roomname"; + type = "text-single"; + label = "Name"; + value = get_name(event.room) or ""; + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_roomname"]; + if new ~= nil and set_name(event.room, new) then + event.status_codes["104"] = true; + end +end); + +return { + get = get_name; + set = set_name; +}; -- cgit v1.2.3 From 9b57ba8391c07c5c5931641725b45760823570b7 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 2 Apr 2014 18:37:52 -0400 Subject: plugins/muc/occupant.lib: Don't allow an unavailable session to be the primary jid --- plugins/muc/occupant.lib.lua | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/occupant.lib.lua b/plugins/muc/occupant.lib.lua index b4b12390..5cecb139 100644 --- a/plugins/muc/occupant.lib.lua +++ b/plugins/muc/occupant.lib.lua @@ -36,7 +36,8 @@ end local function copy_occupant(occupant) local sessions = {}; for full_jid, presence_stanza in pairs(occupant.sessions) do - if presence_stanza.attr.type ~= "unavailable" then + -- Don't keep unavailable presences, as they'll accumulate; unless they're the primary session + if presence_stanza.attr.type ~= "unavailable" or full_jid == occupant.jid then sessions[full_jid] = presence_stanza; end end @@ -49,24 +50,35 @@ local function copy_occupant(occupant) }, occupant_mt); end +-- finds another session to be the primary (there might not be one) +function occupant_mt:choose_new_primary() + for jid, pr in self:each_session() do + if pr.attr.type ~= "unavailable" then + return jid; + end + end + return nil; +end + function occupant_mt:set_session(real_jid, presence_stanza, replace_primary) local pr = get_filtered_presence(presence_stanza); pr.attr.from = self.nick; pr.attr.to = real_jid; self.sessions[real_jid] = pr; - if replace_primary or self.jid == nil then + if replace_primary then self.jid = real_jid; + elseif self.jid == nil or (pr.attr.type == "unavailable" and self.jid == real_jid) then + -- Only leave an unavailable presence as primary when there are no other options + self.jid = self:choose_new_primary() or real_jid; end end function occupant_mt:remove_session(real_jid) -- Delete original session - local presence_stanza = self.sessions[real_jid]; self.sessions[real_jid] = nil; if self.jid == real_jid then - -- find another session to be the primary (might be nil) - self.jid = next(self.sessions); + self.jid = self:choose_new_primary(); end end -- cgit v1.2.3 From 13c9f3da9076f3c1057d9ad6e6dc3a6e874d333c Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 12:25:03 -0400 Subject: plugins/muc/muc.lib: Clean up whois handling - adds functions `can_see_real_jids` and `get_base_presence` - In `publicise_occupant_status`, we don't generate each type of presence until it's used --- plugins/muc/muc.lib.lua | 65 +++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 26 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 15e3f9c9..5b160185 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -217,41 +217,53 @@ function room_mt:broadcast(stanza, cond_func) end end --- Broadcasts an occupant's presence to the whole room --- Takes (and modifies) the x element that goes into the stanzas -function room_mt:publicise_occupant_status(occupant, full_x, actor, reason) - local anon_x; - local has_anonymous = self:get_whois() ~= "anyone"; - if has_anonymous then - anon_x = st.clone(full_x); - self:build_item_list(occupant, anon_x, true, nil, actor, reason); +local function can_see_real_jids(whois, occupant) + if whois == "anyone" then + return true; + elseif whois == "moderators" then + return valid_roles[occupant.role or "none"] >= valid_roles.moderator; end - self:build_item_list(occupant,full_x, false, nil, actor, reason); +end - -- General populance - local full_p +local function get_base_presence(occupant) if occupant.role ~= nil then -- Try to use main jid's presence local pr = occupant:get_presence(); if pr ~= nil then - full_p = st.clone(pr); + return st.clone(pr); end end - if full_p == nil then - full_p = st.presence{from=occupant.nick; type="unavailable"}; - end - local anon_p; - if has_anonymous then - anon_p = st.clone(full_p); - anon_p:add_child(anon_x); + return st.presence {from = occupant.nick; type = "unavailable";}; +end + +-- Broadcasts an occupant's presence to the whole room +-- Takes the x element that goes into the stanzas +function room_mt:publicise_occupant_status(occupant, base_x, actor, reason) + -- Build real jid and (optionally) occupant jid template presences + local function get_presence(is_anonymous) + local x = st.clone(base_x); + self:build_item_list(occupant, x, is_anonymous, actor, reason); + return get_base_presence(occupant):add_child(x), x; + end + local full_p, full_x = get_presence(false); + local anon_p, anon_x; + local function get_anon_p() + if anon_p == nil then + anon_p, anon_x = get_presence(true); + end + return anon_p, anon_x; end - full_p:add_child(full_x); + local whois = self:get_whois(); + + -- General populance for nick, n_occupant in self:each_occupant() do - if nick ~= occupant.nick or n_occupant.role == nil then - local pr = full_p; - if has_anonymous and n_occupant.role ~= "moderator" and occupant.bare_jid ~= n_occupant.bare_jid then - pr = anon_p; + if nick ~= occupant.nick then + local pr; + if can_see_real_jids(whois, occupant) or occupant.bare_jid == n_occupant.bare_jid then + pr = full_p; + else + pr = get_anon_p(); end self:route_to_occupant(n_occupant, pr); end @@ -279,11 +291,12 @@ end function room_mt:send_occupant_list(to, filter) local to_bare = jid_bare(to); local is_anonymous = true; - if self:get_whois() ~= "anyone" then + local whois = self:get_whois(); + if whois ~= "anyone" then local affiliation = self:get_affiliation(to); if affiliation ~= "admin" and affiliation ~= "owner" then local occupant = self:get_occupant_by_real_jid(to); - if not occupant or occupant.role ~= "moderator" then + if not occupant or can_see_real_jids(whois, occupant) then is_anonymous = false; end end -- cgit v1.2.3 From 25e6558aafd036d7fddbf9ad95c39e2cda5b49b9 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 12:44:27 -0400 Subject: plugins/muc/muc.lib: Refactor of change-nick presence handling - Allow `nick` to be passed to `publicise_occupant_status`. - Moves multi-session handling to a more 'edge-case' area of code --- plugins/muc/muc.lib.lua | 66 +++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 35 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 5b160185..353a7920 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -238,11 +238,11 @@ end -- Broadcasts an occupant's presence to the whole room -- Takes the x element that goes into the stanzas -function room_mt:publicise_occupant_status(occupant, base_x, actor, reason) +function room_mt:publicise_occupant_status(occupant, base_x, nick, actor, reason) -- Build real jid and (optionally) occupant jid template presences local function get_presence(is_anonymous) local x = st.clone(base_x); - self:build_item_list(occupant, x, is_anonymous, actor, reason); + self:build_item_list(occupant, x, is_anonymous, nick, actor, reason); return get_base_presence(occupant):add_child(x), x; end local full_p, full_x = get_presence(false); @@ -277,13 +277,11 @@ function room_mt:publicise_occupant_status(occupant, base_x, actor, reason) else -- use their own presences as templates for full_jid, pr in occupant:each_session() do - if pr.attr.type ~= "unavailable" then - pr = st.clone(pr); - pr.attr.to = full_jid; - -- You can always see your own full jids - pr:add_child(full_x); - self:route_stanza(pr); - end + pr = st.clone(pr); + pr.attr.to = full_jid; + -- You can always see your own full jids + pr:add_child(full_x); + self:route_stanza(pr); end end end @@ -671,46 +669,44 @@ function room_mt:handle_presence_to_occupant(origin, stanza) -- Send presence stanza about original occupant if orig_occupant ~= nil and orig_occupant ~= dest_occupant then local orig_x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}); - + local dest_nick; if dest_occupant == nil then -- Session is leaving log("debug", "session %s is leaving occupant %s", real_jid, orig_occupant.nick); orig_occupant:set_session(real_jid, stanza); else log("debug", "session %s is changing from occupant %s to %s", real_jid, orig_occupant.nick, dest_occupant.nick); - orig_occupant:remove_session(real_jid); -- If we are moving to a new nick; we don't want to get our own presence - - local dest_nick = select(3, jid_split(dest_occupant.nick)); - local affiliation = self:get_affiliation(bare_jid); - - -- This session + local generated_unavail = st.presence {from = orig_occupant.nick, to = real_jid, type = "unavailable"}; + orig_occupant:set_session(real_jid, generated_unavail); + dest_nick = select(3, jid_split(dest_occupant.nick)); if not is_first_dest_session then -- User is swapping into another pre-existing session log("debug", "session %s is swapping into multisession %s, showing it leave.", real_jid, dest_occupant.nick); -- Show the other session leaving local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}) :tag("status"):text("you are joining pre-existing session " .. dest_nick):up(); - add_item(x, affiliation, "none"); + add_item(x, self:get_affiliation(bare_jid), "none"); local pr = st.presence{from = dest_occupant.nick, to = real_jid, type = "unavailable"} :add_child(x); self:route_stanza(pr); - else - if is_last_orig_session then -- User is moving to a new session - log("debug", "no sessions in %s left; marking as nick change", orig_occupant.nick); - -- Everyone gets to see this as a nick change - local jid = self:get_whois() ~= "anyone" and real_jid or nil; -- FIXME: mods should see real jids - add_item(orig_x, affiliation, orig_occupant.role, jid, dest_nick); - orig_x:tag("status", {code = "303";}):up(); - end end - -- The session itself always sees a nick change - local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}); - add_item(x, affiliation, orig_occupant.role, real_jid, dest_nick); - -- self:build_item_list(orig_occupant, x, false); -- COMPAT - x:tag("status", {code = "303";}):up(); - x:tag("status", {code = "110";}):up(); - self:route_stanza(st.presence{from = orig_occupant.nick, to = real_jid, type = "unavailable"}:add_child(x)); + if is_first_dest_session and is_last_orig_session then -- Normal nick change + log("debug", "no sessions in %s left; publically marking as nick change", orig_occupant.nick); + orig_x:tag("status", {code = "303";}):up(); + else -- The session itself always needs to see a nick change + -- don't want to get our old nick's available presence, + -- so remove our session from there, and manually generate an unavailable + orig_occupant:remove_session(real_jid); + log("debug", "generating nick change for %s", real_jid); + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";}); + -- self:build_item_list(orig_occupant, x, false, dest_nick); -- COMPAT: clients get confused if they see other items besides their own + add_item(x, self:get_affiliation(bare_jid), orig_occupant.role, real_jid, dest_nick); + x:tag("status", {code = "303";}):up(); + x:tag("status", {code = "110";}):up(); + self:route_stanza(generated_unavail:add_child(x)); + dest_nick = nil; -- set dest_nick to nil; so general populance doesn't see it for whole orig_occupant + end end self:save_occupant(orig_occupant); - self:publicise_occupant_status(orig_occupant, orig_x); + self:publicise_occupant_status(orig_occupant, orig_x, dest_nick); if is_last_orig_session then module:fire_event("muc-occupant-left", {room = self; nick = orig_occupant.nick;}); @@ -1381,7 +1377,7 @@ function room_mt:set_affiliation(actor, jid, affiliation, reason) end local is_semi_anonymous = self:get_whois() == "moderators"; for occupant, old_role in pairs(occupants_updated) do - self:publicise_occupant_status(occupant, x, actor, reason); + self:publicise_occupant_status(occupant, x, nil, actor, reason); if is_semi_anonymous and (old_role == "moderator" and occupant.role ~= "moderator") or (old_role ~= "moderator" and occupant.role == "moderator") then -- Has gained or lost moderator status @@ -1442,7 +1438,7 @@ function room_mt:set_role(actor, occupant_jid, role, reason) end occupant.role = role; self:save_occupant(occupant); - self:publicise_occupant_status(occupant, x, actor, reason); + self:publicise_occupant_status(occupant, x, nil, actor, reason); return true; end -- cgit v1.2.3 From b1cf3db9a1227bd6a2d21d27b81a65d597e5b7f8 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 14:23:06 -0400 Subject: plugins/muc/muc.lib: Remember to coerce nil role to "none" --- plugins/muc/muc.lib.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 353a7920..20cbe04d 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -163,10 +163,11 @@ local function add_item(x, affiliation, role, jid, nick, actor, reason) x:up(); return x end + -- actor is (real) jid function room_mt:build_item_list(occupant, x, is_anonymous, nick, actor, reason) - local affiliation = self:get_affiliation(occupant.bare_jid); - local role = occupant.role; + local affiliation = self:get_affiliation(occupant.bare_jid) or "none"; + local role = occupant.role or "none"; local actor_attr; if actor then actor_attr = {nick = select(3,jid_split(self:get_occupant_jid(actor)))}; -- cgit v1.2.3 From bae30c03d41b80b2bd10ca2d0c9b60d2a6913bc3 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 14:24:27 -0400 Subject: plugins/muc: Move `whois` code to seperate file --- plugins/muc/muc.lib.lua | 54 ++++---------------------------------- plugins/muc/whois.lib.lua | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 49 deletions(-) create mode 100644 plugins/muc/whois.lib.lua (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 20cbe04d..7a8464e5 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -420,9 +420,6 @@ end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_hidden() and "muc_hidden" or "muc_public"}):up(); end); -module:hook("muc-disco#info", function(event) - event.reply:tag("feature", {var = event.room:get_whois() ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up(); -end); module:hook("muc-disco#info", function(event) local count = 0; for _ in event.room:each_occupant() do count = count + 1; end table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }); @@ -543,20 +540,6 @@ function room_mt:set_historylength(length) self._data.history_length = length; end - -local valid_whois = { moderators = true, anyone = true }; - -function room_mt:set_whois(whois) - if valid_whois[whois] and self._data.whois ~= whois then - self._data.whois = whois; - if self.save then self:save(true); end - end -end - -function room_mt:get_whois() - return self._data.whois; -end - -- Give the room creator owner affiliation module:hook("muc-room-pre-create", function(event) event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); @@ -872,18 +855,6 @@ module:hook("muc-config-form", function(event) value = event.room:get_changesubject() }); end); -module:hook("muc-config-form", function(event) - local whois = event.room:get_whois(); - table.insert(event.form, { - name = 'muc#roomconfig_whois', - type = 'list-single', - label = 'Who May Discover Real JIDs?', - value = { - { value = 'moderators', label = 'Moderators Only', default = whois == 'moderators' }, - { value = 'anyone', label = 'Anyone', default = whois == 'anyone' } - } - }); -end); module:hook("muc-config-form", function(event) table.insert(event.form, { name = 'muc#roomconfig_moderatedroom', @@ -967,12 +938,6 @@ end); module:hook("muc-config-submitted", function(event) event.update_option("historylength", "muc#roomconfig_historylength"); end); -module:hook("muc-config-submitted", function(event) - if event.update_option("whois", "muc#roomconfig_whois", valid_whois) then - local code = (event.room:get_whois() == 'moderators') and "173" or "172"; - event.status_codes[code] = true; - end -end); -- Removes everyone from the room function room_mt:clear(x) @@ -1218,21 +1183,9 @@ module:hook("muc-invite", function(event) :up(); end); --- Mask 'from' jid as occupant jid if room is anonymous -module:hook("muc-invite", function(event) - local room, stanza = event.room, event.stanza; - if room:get_whois() == "moderators" and room:get_default_role(room:get_affiliation(stanza.attr.to)) ~= "moderator" then - local invite = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); - local occupant_jid = room:get_occupant_jid(invite.attr.from); - if occupant_jid ~= nil then -- FIXME: This will expose real jid if inviter is not in room - invite.attr.from = occupant_jid; - end - end -end, 50); - -- When an invite is sent; add an affiliation for the invitee module:hook("muc-invite", function(event) - local room, stanza = event.room, event.stanza + local room, stanza = event.room, event.stanza; local invitee = stanza.attr.to if room:get_members_only() and not room:get_affiliation(invitee) then local from = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite").attr.from @@ -1455,6 +1408,10 @@ local password = module:require "muc/password"; room_mt.get_password = password.get; room_mt.set_password = password.set; +local whois = module:require "muc/whois"; +room_mt.get_whois = whois.get; +room_mt.set_whois = whois.set; + local _M = {}; -- module "muc" function _M.new_room(jid, config) @@ -1463,7 +1420,6 @@ function _M.new_room(jid, config) _jid_nick = {}; _occupants = {}; _data = { - whois = 'moderators'; history_length = math.min((config and config.history_length) or default_history_length, max_history_length); }; diff --git a/plugins/muc/whois.lib.lua b/plugins/muc/whois.lib.lua new file mode 100644 index 00000000..f89e6087 --- /dev/null +++ b/plugins/muc/whois.lib.lua @@ -0,0 +1,67 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local valid_whois = { + moderators = true; + anyone = true; +}; + +local function get_whois(room) + return room._data.whois or "moderators"; +end + +local function set_whois(room, whois) + assert(valid_whois[whois], "Invalid whois value") + if get_whois(room) == whois then return false; end + room._data.whois = whois; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = get_whois(event.room) ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up(); +end); + +module:hook("muc-config-form", function(event) + local whois = get_whois(event.room); + table.insert(event.form, { + name = 'muc#roomconfig_whois', + type = 'list-single', + label = 'Who May Discover Real JIDs?', + value = { + { value = 'moderators', label = 'Moderators Only', default = whois == 'moderators' }, + { value = 'anyone', label = 'Anyone', default = whois == 'anyone' } + } + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_whois"]; + if new ~= nil and set_whois(event.room, new) then + local code = (new == 'moderators') and "173" or "172"; + event.status_codes[code] = true; + end +end); + +-- Mask 'from' jid as occupant jid if room is anonymous +module:hook("muc-invite", function(event) + local room, stanza = event.room, event.stanza; + if get_whois(room) == "moderators" and room:get_default_role(room:get_affiliation(stanza.attr.to)) ~= "moderator" then + local invite = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); + local occupant_jid = room:get_occupant_jid(invite.attr.from); + if occupant_jid ~= nil then -- FIXME: This will expose real jid if inviter is not in room + invite.attr.from = occupant_jid; + end + end +end, 50); + +return { + get = get_whois; + set = set_whois; +}; -- cgit v1.2.3 From b661ecb24f5e68a3a7af2532f8e91189e305fb82 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 15:14:52 -0400 Subject: plugins/muc: Move history to an external module This resulted in the split up of the main muc-occupant-joined event handler into 3 seperate ones, handling occupant list, history and subject --- plugins/muc/history.lib.lua | 161 ++++++++++++++++++++++++++++++++++++++++++ plugins/muc/muc.lib.lua | 167 ++++++-------------------------------------- 2 files changed, 182 insertions(+), 146 deletions(-) create mode 100644 plugins/muc/history.lib.lua (limited to 'plugins') diff --git a/plugins/muc/history.lib.lua b/plugins/muc/history.lib.lua new file mode 100644 index 00000000..417d62a8 --- /dev/null +++ b/plugins/muc/history.lib.lua @@ -0,0 +1,161 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local gettime = os.time; +local datetime = require "util.datetime"; +local st = require "util.stanza"; + +local default_history_length, max_history_length = 20, math.huge; + +local function set_max_history_length(_max_history_length) + max_history_length = _max_history_length or math.huge; +end + +local function get_historylength(room) + return math.min(room._data.history_length or default_history_length, max_history_length); +end + +local function set_historylength(room, length) + length = assert(tonumber(length), "Length not a valid number"); + if length == default_history_length then length = nil; end + room._data.history_length = length; + return true; +end + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_historylength"; + type = "text-single"; + label = "Maximum Number of History Messages Returned by Room"; + value = tostring(get_historylength(event.room)); + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_historylength"]; + if new ~= nil and set_historylength(event.room, new) then + event.status_codes["104"] = true; + end +end); + +local function parse_history(stanza) + local x_tag = stanza:get_child("x", "http://jabber.org/protocol/muc"); + local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc"); + if not history_tag then + return nil, default_history_length, nil; + end + + local maxchars = tonumber(history_tag.attr.maxchars); + + local maxstanzas = tonumber(history_tag.attr.maxstanzas); + + -- messages received since the UTC datetime specified + local since = history_tag.attr.since; + if since then + since = datetime.parse(since); + end + + -- messages received in the last "X" seconds. + local seconds = tonumber(history_tag.attr.seconds); + if seconds then + seconds = gettime() - seconds; + if since then + since = math.max(since, seconds); + else + since = seconds; + end + end + + return maxchars, maxstanzas, since; +end + +module:hook("muc-get-history", function(event) + local room = event.room; + local history = room._data["history"]; -- send discussion history + if not history then return nil end + local history_len = #history; + + local to = event.to; + local maxchars = event.maxchars; + local maxstanzas = event.maxstanzas or history_len; + local since = event.since; + local n = 0; + local charcount = 0; + for i=history_len,1,-1 do + local entry = history[i]; + if maxchars then + if not entry.chars then + entry.stanza.attr.to = ""; + entry.chars = #tostring(entry.stanza); + end + charcount = charcount + entry.chars + #to; + if charcount > maxchars then break; end + end + if since and since > entry.timestamp then break; end + if n + 1 > maxstanzas then break; end + n = n + 1; + end + + local i = history_len-n+1 + function event:next_stanza() + if i > history_len then return nil end + local entry = history[i]; + local msg = entry.stanza; + msg.attr.to = to; + i = i + 1; + return msg; + end + return true; +end); + +local function send_history(room, stanza) + local maxchars, maxstanzas, since = parse_history(stanza); + local event = { + room = room; + to = stanza.attr.from; -- `to` is required to calculate the character count for `maxchars` + maxchars = maxchars, maxstanzas = maxstanzas, since = since; + next_stanza = function() end; -- events should define this iterator + }; + module:fire_event("muc-get-history", event); + for msg in event.next_stanza, event do + room:route_stanza(msg); + end +end + +-- Send history on join +module:hook("muc-occupant-joined", function(event) + send_history(event.room, event.stanza); +end, 50); -- Between occupant list (80) and subject(20) + +-- add to history +module:hook("muc-broadcast-message", function(event) + local historic = event.stanza:get_child("body"); + if historic then + local room = event.room + local history = room._data["history"]; + if not history then history = {}; room._data["history"] = history; end + local stanza = st.clone(event.stanza); + stanza.attr.to = ""; + local ts = gettime(); + local stamp = datetime.datetime(ts); + stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp}):up(); -- XEP-0203 + stanza:tag("x", {xmlns = "jabber:x:delay", from = module.host, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) + local entry = { stanza = stanza, timestamp = ts }; + table.insert(history, entry); + while #history > get_historylength(room) do table.remove(history, 1) end + end +end); + +return { + set_max_length = set_max_history_length; + parse_history = parse_history; + send = send_history; + get_length = get_historylength; + set_length = set_historylength; +}; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 7a8464e5..89953615 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -11,13 +11,8 @@ local select = select; local pairs, ipairs = pairs, ipairs; local next = next; local setmetatable = setmetatable; -local t_insert, t_remove = table.insert, table.remove; - -local gettime = os.time; -local datetime = require "util.datetime"; local dataform = require "util.dataforms"; - local jid_split = require "util.jid".split; local jid_bare = require "util.jid".bare; local jid_prep = require "util.jid".prep; @@ -28,7 +23,6 @@ local md5 = require "util.hashes".md5; local occupant_lib = module:require "muc/occupant" -local default_history_length, max_history_length = 20, math.huge; local is_kickable_error do local kickable_error_conditions = { @@ -185,29 +179,11 @@ function room_mt:build_item_list(occupant, x, is_anonymous, nick, actor, reason) return x end -function room_mt:broadcast_message(stanza, historic) - module:fire_event("muc-broadcast-message", {room = self, stanza = stanza, historic = historic}); +function room_mt:broadcast_message(stanza) + module:fire_event("muc-broadcast-message", {room = self, stanza = stanza}); self:broadcast(stanza); end --- add to history -module:hook("muc-broadcast-message", function(event) - if event.historic then - local room = event.room - local history = room._data['history']; - if not history then history = {}; room._data['history'] = history; end - local stanza = st.clone(event.stanza); - stanza.attr.to = ""; - local ts = gettime(); - local stamp = datetime.datetime(ts); - stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp}):up(); -- XEP-0203 - stanza:tag("x", {xmlns = "jabber:x:delay", from = module.host, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) - local entry = { stanza = stanza, timestamp = ts }; - t_insert(history, entry); - while #history > room:get_historylength() do t_remove(history, 1) end - end -end); - -- Broadcast a stanza to all occupants in the room. -- optionally checks conditional called with (nick, occupant) function room_mt:broadcast(stanza, cond_func) @@ -312,90 +288,6 @@ function room_mt:send_occupant_list(to, filter) end end -local function parse_history(stanza) - local x_tag = stanza:get_child("x", "http://jabber.org/protocol/muc"); - local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc"); - if not history_tag then - return nil, 20, nil - end - - local maxchars = tonumber(history_tag.attr.maxchars); - - local maxstanzas = tonumber(history_tag.attr.maxstanzas); - - -- messages received since the UTC datetime specified - local since = history_tag.attr.since; - if since then - since = datetime.parse(since); - end - - -- messages received in the last "X" seconds. - local seconds = tonumber(history_tag.attr.seconds); - if seconds then - seconds = gettime() - seconds - if since then - since = math.max(since, seconds); - else - since = seconds; - end - end - - return maxchars, maxstanzas, since -end - -module:hook("muc-get-history", function(event) - local room = event.room - local history = room._data['history']; -- send discussion history - if not history then return nil end - local history_len = #history - - local to = event.to - local maxchars = event.maxchars - local maxstanzas = event.maxstanzas or history_len - local since = event.since - local n = 0; - local charcount = 0; - for i=history_len,1,-1 do - local entry = history[i]; - if maxchars then - if not entry.chars then - entry.stanza.attr.to = ""; - entry.chars = #tostring(entry.stanza); - end - charcount = charcount + entry.chars + #to; - if charcount > maxchars then break; end - end - if since and since > entry.timestamp then break; end - if n + 1 > maxstanzas then break; end - n = n + 1; - end - - local i = history_len-n+1 - function event:next_stanza() - if i > history_len then return nil end - local entry = history[i] - local msg = entry.stanza - msg.attr.to = to; - i = i + 1 - return msg - end - return true; -end); - -function room_mt:send_history(stanza) - local maxchars, maxstanzas, since = parse_history(stanza) - local event = { - room = self; - to = stanza.attr.from; -- `to` is required to calculate the character count for `maxchars` - maxchars = maxchars, maxstanzas = maxstanzas, since = since; - next_stanza = function() end; -- events should define this iterator - } - module:fire_event("muc-get-history", event) - for msg in event.next_stanza , event do - self:route_stanza(msg); - end -end - function room_mt:get_disco_info(stanza) local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#info"); local form = dataform.new { @@ -451,7 +343,7 @@ function room_mt:set_subject(current_nick, subject) self._data['subject_from'] = current_nick; if self.save then self:save(); end local msg = create_subject_message(current_nick, subject); - self:broadcast_message(msg, false); + self:broadcast_message(msg); return true; end @@ -529,16 +421,6 @@ end function room_mt:get_changesubject() return self._data.changesubject; end -function room_mt:get_historylength() - return self._data.history_length or default_history_length; -end -function room_mt:set_historylength(length) - length = math.min(tonumber(length) or default_history_length, max_history_length or math.huge); - if length == default_history_length then - length = nil; - end - self._data.history_length = length; -end -- Give the room creator owner affiliation module:hook("muc-room-pre-create", function(event) @@ -569,16 +451,19 @@ module:hook("muc-occupant-pre-join", function(event) end end, -10); +-- Send occupant list to newly joined user module:hook("muc-occupant-joined", function(event) - local room, stanza = event.room, event.stanza; - local real_jid = stanza.attr.from; - room:send_occupant_list(real_jid, function(nick, occupant) + local real_jid = event.stanza.attr.from; + event.room:send_occupant_list(real_jid, function(nick, occupant) -- Don't include self return occupant:get_presence(real_jid) == nil; end); - room:send_history(stanza); - room:send_subject(real_jid); -end, -1); +end, 80); + +-- Send subject to joining user +module:hook("muc-occupant-joined", function(event) + event.room:send_subject(event.stanza.attr.from); +end, 20); function room_mt:handle_presence_to_occupant(origin, stanza) local type = stanza.attr.type; @@ -871,14 +756,6 @@ module:hook("muc-config-form", function(event) value = event.room:get_members_only() }); end); -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_historylength', - type = 'text-single', - label = 'Maximum Number of History Messages Returned by Room', - value = tostring(event.room:get_historylength()) - }); -end); function room_mt:process_form(origin, stanza) local form = stanza.tags[1]:get_child("x", "jabber:x:data"); @@ -913,7 +790,7 @@ function room_mt:process_form(origin, stanza) msg:tag("status", {code = code;}):up(); end msg:up(); - self:broadcast_message(msg, false) + self:broadcast_message(msg); end else origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); @@ -935,9 +812,6 @@ end); module:hook("muc-config-submitted", function(event) event.update_option("changesubject", "muc#roomconfig_changesubject"); end); -module:hook("muc-config-submitted", function(event) - event.update_option("historylength", "muc#roomconfig_historylength"); -end); -- Removes everyone from the room function room_mt:clear(x) @@ -1099,7 +973,7 @@ function room_mt:handle_groupchat_to_room(origin, stanza) origin.send(st.error_reply(stanza, "auth", "forbidden")); end else - self:broadcast_message(stanza, self:get_historylength() > 0 and stanza:get_child("body")); + self:broadcast_message(stanza); end stanza.attr.from = from; return true; @@ -1412,25 +1286,26 @@ local whois = module:require "muc/whois"; room_mt.get_whois = whois.get; room_mt.set_whois = whois.set; +local history = module:require "muc/history"; +room_mt.send_history = history.send; +room_mt.get_historylength = history.get_length; +room_mt.set_historylength = history.set_length; + local _M = {}; -- module "muc" +_M.set_max_history_length = history.set_max_length; + function _M.new_room(jid, config) return setmetatable({ jid = jid; _jid_nick = {}; _occupants = {}; _data = { - history_length = math.min((config and config.history_length) - or default_history_length, max_history_length); }; _affiliations = {}; }, room_mt); end -function _M.set_max_history_length(_max_history_length) - max_history_length = _max_history_length or math.huge; -end - _M.room_mt = room_mt; return _M; -- cgit v1.2.3 From 185f0d33268c363466e638cbf14bd4ba492be13d Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 16:04:04 -0400 Subject: plugins/muc/muc.lib: Move occupancy check to later in `deconstruct_stanza_id`: As vcards are from the bare jid, you need to use the `from_jid` out of the encoded `id` --- plugins/muc/muc.lib.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 89953615..694e13ff 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -622,13 +622,14 @@ function room_mt:handle_iq_to_occupant(origin, stanza) local from, to = stanza.attr.from, stanza.attr.to; local type = stanza.attr.type; local id = stanza.attr.id; - local current_nick = self:get_occupant_jid(from); local occupant = self:get_occupant_by_nick(to); if (type == "error" or type == "result") then do -- deconstruct_stanza_id - if not current_nick or not occupant then return nil; end + if not occupant then return nil; end local from_jid, id, to_jid_hash = (base64.decode(stanza.attr.id) or ""):match("^(.+)%z(.*)%z(.+)$"); if not(from == from_jid or from == jid_bare(from_jid)) then return nil; end + local from_occupant_jid = self:get_occupant_jid(from_jid); + if from_occupant_jid == nil then return nil; end local session_jid for to_jid in occupant:each_session() do if md5(to_jid) == to_jid_hash then @@ -637,13 +638,14 @@ function room_mt:handle_iq_to_occupant(origin, stanza) end end if session_jid == nil then return nil; end - stanza.attr.from, stanza.attr.to, stanza.attr.id = current_nick, session_jid, id + stanza.attr.from, stanza.attr.to, stanza.attr.id = from_jid, session_jid, id; end log("debug", "%s sent private iq stanza to %s (%s)", from, to, stanza.attr.to); self:route_stanza(stanza); stanza.attr.from, stanza.attr.to, stanza.attr.id = from, to, id; return true; else -- Type is "get" or "set" + local current_nick = self:get_occupant_jid(from); if not current_nick then origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); return true; -- cgit v1.2.3 From e14f89d1d228c410f3a67cfd59792f660ad9562d Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 16:10:43 -0400 Subject: plugins/muc/muc.lib: Use util.iterators to count occupants --- plugins/muc/muc.lib.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 694e13ff..59069cae 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -13,6 +13,7 @@ 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; @@ -313,7 +314,7 @@ module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_hidden() and "muc_hidden" or "muc_public"}):up(); end); module:hook("muc-disco#info", function(event) - local count = 0; for _ in event.room:each_occupant() do count = count + 1; end + local count = iterators.count(event.room:each_occupant()); table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }); end); -- cgit v1.2.3 From adb653030ff70cff8ecbc10eb3650cc6ff79fba9 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 17:09:04 -0400 Subject: plugins/muc: Move valid_roles, valid_affiliations and is_kickable_error to new muc/util module --- plugins/muc/muc.lib.lua | 38 +++----------------------------------- plugins/muc/util.lib.lua | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 35 deletions(-) create mode 100644 plugins/muc/util.lib.lua (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 59069cae..6f19cd53 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -23,26 +23,9 @@ local base64 = require "util.encodings".base64; local md5 = require "util.hashes".md5; local occupant_lib = module:require "muc/occupant" - - -local is_kickable_error do - local kickable_error_conditions = { - ["gone"] = true; - ["internal-server-error"] = true; - ["item-not-found"] = true; - ["jid-malformed"] = true; - ["recipient-unavailable"] = true; - ["redirect"] = true; - ["remote-server-not-found"] = true; - ["remote-server-timeout"] = true; - ["service-unavailable"] = true; - ["malformed error"] = true; - }; - function is_kickable_error(stanza) - local cond = select(2, stanza:get_error()) or "malformed error"; - return kickable_error_conditions[cond]; - end -end +local muc_util = module:require "muc/util"; +local is_kickable_error = muc_util.is_kickable_error; +local valid_roles, valid_affiliations = muc_util.valid_roles, muc_util.valid_affiliations; local room_mt = {}; room_mt.__index = room_mt; @@ -55,21 +38,6 @@ function room_mt:get_occupant_jid(real_jid) return self._jid_nick[real_jid] end -local valid_affiliations = { - outcast = 0; - none = 1; - member = 2; - admin = 3; - owner = 4; -}; - -local valid_roles = { - none = 0; - visitor = 1; - participant = 2; - moderator = 3; -}; - function room_mt:get_default_role(affiliation) if affiliation == "owner" or affiliation == "admin" then return "moderator"; diff --git a/plugins/muc/util.lib.lua b/plugins/muc/util.lib.lua new file mode 100644 index 00000000..90a3a183 --- /dev/null +++ b/plugins/muc/util.lib.lua @@ -0,0 +1,44 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local _M = {}; + +_M.valid_affiliations = { + outcast = -1; + none = 0; + member = 1; + admin = 2; + owner = 3; +}; + +_M.valid_roles = { + none = 0; + visitor = 1; + participant = 2; + moderator = 3; +}; + +local kickable_error_conditions = { + ["gone"] = true; + ["internal-server-error"] = true; + ["item-not-found"] = true; + ["jid-malformed"] = true; + ["recipient-unavailable"] = true; + ["redirect"] = true; + ["remote-server-not-found"] = true; + ["remote-server-timeout"] = true; + ["service-unavailable"] = true; + ["malformed error"] = true; +}; +function _M.is_kickable_error(stanza) + local cond = select(2, stanza:get_error()) or "malformed error"; + return kickable_error_conditions[cond]; +end + +return _M; -- cgit v1.2.3 From d9474e875f5ea22a41f081e9586840ca70739db8 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 18:10:16 -0400 Subject: plugins/muc/muc.lib: Fix getting a list of occupants by role (it was sending presences instead of items inside an iq) --- plugins/muc/muc.lib.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 6f19cd53..fdddcfbe 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -113,7 +113,6 @@ function room_mt:route_to_occupant(occupant, stanza) stanza.attr.to = to; end --- Adds an item to an "x" element. -- actor is the attribute table local function add_item(x, affiliation, role, jid, nick, actor, reason) x:tag("item", {affiliation = affiliation; role = role; jid = jid; nick = nick;}) @@ -874,9 +873,17 @@ function room_mt:handle_admin_query_get_command(origin, stanza) end elseif _rol and not _aff then local role = self:get_role(self:get_occupant_jid(actor)) or self:get_default_role(affiliation); - if role == "moderator" then + if valid_roles[role or "none"] >= valid_roles.moderator then if _rol == "none" then _rol = nil; end - self:send_occupant_list(actor, function(occupant_jid, occupant) return occupant.role == _rol end); + local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); + -- TODO: whois check here? (though fully anonymous rooms are not supported) + for occupant_jid, occupant in self:each_occupant() do + if occupant.role == _rol then + local nick = select(3,jid_split(occupant_jid)); + self:build_item_list(occupant, reply, false, nick); + end + end + origin.send(reply:up()); return true; else origin.send(st.error_reply(stanza, "auth", "forbidden")); -- cgit v1.2.3 From 0b749352c28b79d5ebc582d479591adb8aa7ae2f Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Apr 2014 18:36:28 -0400 Subject: plugins/muc/muc.lib: Turn get_default_role into an event --- plugins/muc/muc.lib.lua | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index fdddcfbe..4f8aa15d 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -39,16 +39,30 @@ function room_mt:get_occupant_jid(real_jid) end function room_mt:get_default_role(affiliation) - if affiliation == "owner" or affiliation == "admin" then + local role = module:fire_event("muc-get-default-role", { + room = self; + affiliation = affiliation; + affiliation_rank = valid_affiliations[affiliation or "none"]; + }); + return role, valid_roles[role or "none"]; +end +module:hook("muc-get-default-role", function(event) + if event.affiliation_rank >= valid_affiliations.admin then return "moderator"; - elseif affiliation == "member" then + elseif event.affiliation_rank >= valid_affiliations.member then return "participant"; - elseif not affiliation then - if not self:get_members_only() then - return self:get_moderated() and "visitor" or "participant"; - end end -end +end); +module:hook("muc-get-default-role", function(event) + if not event.affiliation and event.room:get_members_only() then + return false; + end +end); +module:hook("muc-get-default-role", function(event) + if not event.affiliation then + return event.room:get_moderated() and "visitor" or "participant"; + end +end, -1); --- Occupant functions function room_mt:new_occupant(bare_real_jid, nick) -- cgit v1.2.3 From cd72b2acc4eb00c11c30eab62e1cbf496996c36e Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 4 Apr 2014 11:20:20 -0400 Subject: plugins/muc/muc.lib: Move members_only into seperate file --- plugins/muc/members_only.lib.lua | 89 ++++++++++++++++++++++++++++++++++++++++ plugins/muc/muc.lib.lua | 66 ++--------------------------- 2 files changed, 93 insertions(+), 62 deletions(-) create mode 100644 plugins/muc/members_only.lib.lua (limited to 'plugins') diff --git a/plugins/muc/members_only.lib.lua b/plugins/muc/members_only.lib.lua new file mode 100644 index 00000000..84a17699 --- /dev/null +++ b/plugins/muc/members_only.lib.lua @@ -0,0 +1,89 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local muc_util = module:require "muc/util"; +local valid_roles, valid_affiliations = muc_util.valid_roles, muc_util.valid_affiliations; + +local function get_members_only(room) + return room._data.members_only; +end + +local function set_members_only(room, members_only) + members_only = members_only and true or nil; + if room._data.members_only == members_only then return false; end + room._data.members_only = members_only; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = get_members_only(event.room) and "muc_membersonly" or "muc_open"}):up(); +end); + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_membersonly"; + type = "boolean"; + label = "Make Room Members-Only?"; + value = get_members_only(event.room); + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_membersonly"]; + if new ~= nil and set_members_only(event.room, new) then + event.status_codes["104"] = true; + end +end); + +-- No affiliation => role of "none" +module:hook("muc-get-default-role", function(event) + if not event.affiliation and get_members_only(event.room) then + return false; + end +end); + +-- registration required for entering members-only room +module:hook("muc-occupant-pre-join", function(event) + local room, stanza = event.room, event.stanza; + local affiliation = room:get_affiliation(stanza.attr.from); + if affiliation == nil and get_members_only(event.room) then + local reply = st.error_reply(stanza, "auth", "registration-required"):up(); + reply.tags[1].attr.code = "407"; + event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); + return true; + end +end, -5); + +-- Invitation privileges in members-only rooms SHOULD be restricted to room admins; +-- if a member without privileges to edit the member list attempts to invite another user +-- the service SHOULD return a error to the occupant +module:hook("muc-pre-invite", function(event) + local room, stanza = event.room, event.stanza; + if get_members_only(room) and room:get_affiliation(stanza.attr.from) or "none" < valid_affiliations.admin then + event.origin.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + end +end); + +-- When an invite is sent; add an affiliation for the invitee +module:hook("muc-invite", function(event) + local room, stanza = event.room, event.stanza; + local invitee = stanza.attr.to; + if get_members_only(room) and not room:get_affiliation(invitee) then + local from = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite").attr.from; + module:log("debug", "%s invited %s into members only room %s, granting membership", from, invitee, room.jid); + room:set_affiliation(from, invitee, "member", "Invited by " .. from); -- This might fail; ignore for now + end +end); + +return { + get = get_members_only; + set = set_members_only; +}; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 4f8aa15d..d03a32f3 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -53,11 +53,6 @@ module:hook("muc-get-default-role", function(event) return "participant"; end end); -module:hook("muc-get-default-role", function(event) - if not event.affiliation and event.room:get_members_only() then - return false; - end -end); module:hook("muc-get-default-role", function(event) if not event.affiliation then return event.room:get_moderated() and "visitor" or "participant"; @@ -285,9 +280,6 @@ end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up(); end); -module:hook("muc-disco#info", function(event) - event.reply:tag("feature", {var = event.room:get_members_only() and "muc_membersonly" or "muc_open"}):up(); -end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_persistent() and "muc_persistent" or "muc_temporary"}):up(); end); @@ -357,16 +349,6 @@ end function room_mt:get_moderated() return self._data.moderated; end -function room_mt:set_members_only(members_only) - members_only = members_only and true or nil; - if self._data.members_only ~= members_only then - self._data.members_only = members_only; - if self.save then self:save(true); end - end -end -function room_mt:get_members_only() - return self._data.members_only; -end function room_mt:set_persistent(persistent) persistent = persistent and true or nil; if self._data.persistent ~= persistent then @@ -409,17 +391,6 @@ module:hook("muc-room-pre-create", function(event) event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); end, -1); --- registration required for entering members-only room -module:hook("muc-occupant-pre-join", function(event) - local room, stanza = event.room, event.stanza; - local affiliation = room:get_affiliation(stanza.attr.from); - if affiliation == nil and event.room:get_members_only() then - local reply = st.error_reply(stanza, "auth", "registration-required"):up(); - reply.tags[1].attr.code = "407"; - event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); - return true; - end -end, -5); -- check if user is banned module:hook("muc-occupant-pre-join", function(event) @@ -732,14 +703,6 @@ module:hook("muc-config-form", function(event) value = event.room:get_moderated() }); end); -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_membersonly', - type = 'boolean', - label = 'Make Room Members-Only?', - value = event.room:get_members_only() - }); -end); function room_mt:process_form(origin, stanza) local form = stanza.tags[1]:get_child("x", "jabber:x:data"); @@ -787,9 +750,6 @@ end); module:hook("muc-config-submitted", function(event) event.update_option("moderated", "muc#roomconfig_moderatedroom"); end); -module:hook("muc-config-submitted", function(event) - event.update_option("members_only", "muc#roomconfig_membersonly"); -end); module:hook("muc-config-submitted", function(event) event.update_option("public", "muc#roomconfig_publicroom"); end); @@ -997,17 +957,6 @@ module:hook("muc-pre-invite", function(event) end end); --- Invitation privileges in members-only rooms SHOULD be restricted to room admins; --- if a member without privileges to edit the member list attempts to invite another user --- the service SHOULD return a error to the occupant -module:hook("muc-pre-invite", function(event) - local room, stanza = event.room, event.stanza; - if room:get_members_only() and valid_affiliations[room:get_affiliation(stanza.attr.from) or "none"] < valid_affiliations.admin then - event.origin.send(st.error_reply(stanza, "auth", "forbidden")); - return true; - end -end); - function room_mt:handle_mediated_invite(origin, stanza) local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite"); local invitee = jid_prep(payload.attr.to); @@ -1049,17 +998,6 @@ module:hook("muc-invite", function(event) :up(); end); --- When an invite is sent; add an affiliation for the invitee -module:hook("muc-invite", function(event) - local room, stanza = event.room, event.stanza; - local invitee = stanza.attr.to - if room:get_members_only() and not room:get_affiliation(invitee) then - local from = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite").attr.from - log("debug", "%s invited %s into members only room %s, granting membership", from, invitee, room.jid); - room:set_affiliation(from, invitee, "member", "Invited by " .. from); -- This might fail; ignore for now - end -end); - function room_mt:handle_mediated_decline(origin, stanza) local payload = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("decline"); local declinee = jid_prep(payload.attr.to); @@ -1278,6 +1216,10 @@ local whois = module:require "muc/whois"; room_mt.get_whois = whois.get; room_mt.set_whois = whois.set; +local members_only = module:require "muc/members_only"; +room_mt.get_members_only = members_only.get; +room_mt.set_members_only = members_only.set; + local history = module:require "muc/history"; room_mt.send_history = history.send; room_mt.get_historylength = history.get_length; -- cgit v1.2.3 From 0f4dd8a1b5de82bef12ea4b4c5696188fd740be6 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 15 Apr 2014 17:06:04 -0400 Subject: plugins/muc: Move persistent room configuration to own module --- plugins/muc/mod_muc.lua | 8 ++++--- plugins/muc/muc.lib.lua | 29 ++++--------------------- plugins/muc/persistent.lib.lua | 49 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 plugins/muc/persistent.lib.lua (limited to 'plugins') diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 6f6094b4..865e07b9 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -26,6 +26,7 @@ end local muclib = module:require "muc"; local muc_new_room = muclib.new_room; +local persistent = module:require "muc/persistent"; local jid_split = require "util.jid".split; local jid_bare = require "util.jid".bare; local st = require "util.stanza"; @@ -65,8 +66,9 @@ end local function room_save(room, forced) local node = jid_split(room.jid); - persistent_rooms[room.jid] = room._data.persistent; - if room._data.persistent then + local is_persistent = persistent.get(room); + persistent_rooms[room.jid] = is_persistent; + if is_persistent then local history = room._data.history; room._data.history = nil; local data = { @@ -138,7 +140,7 @@ end) module:hook("muc-occupant-left",function(event) local room = event.room - if not next(room._occupants) and not persistent_rooms[room.jid] then -- empty, non-persistent room + if not next(room._occupants) and not persistent.get(room) then -- empty, non-persistent room module:fire_event("muc-room-destroyed", { room = room }); end end); diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index d03a32f3..87f3d69c 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -280,9 +280,6 @@ end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up(); end); -module:hook("muc-disco#info", function(event) - event.reply:tag("feature", {var = event.room:get_persistent() and "muc_persistent" or "muc_temporary"}):up(); -end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_hidden() and "muc_hidden" or "muc_public"}):up(); end); @@ -349,16 +346,6 @@ end function room_mt:get_moderated() return self._data.moderated; end -function room_mt:set_persistent(persistent) - persistent = persistent and true or nil; - if self._data.persistent ~= persistent then - self._data.persistent = persistent; - if self.save then self:save(true); end - end -end -function room_mt:get_persistent() - return self._data.persistent; -end function room_mt:set_hidden(hidden) hidden = hidden and true or nil; if self._data.hidden ~= hidden then @@ -671,14 +658,6 @@ function room_mt:get_form_layout(actor) }); return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form; end -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_persistentroom', - type = 'boolean', - label = 'Make Room Persistent?', - value = event.room:get_persistent() - }); -end); module:hook("muc-config-form", function(event) table.insert(event.form, { name = 'muc#roomconfig_publicroom', @@ -744,9 +723,6 @@ function room_mt:process_form(origin, stanza) end return true; end -module:hook("muc-config-submitted", function(event) - event.update_option("persistent", "muc#roomconfig_persistentroom"); -end); module:hook("muc-config-submitted", function(event) event.update_option("moderated", "muc#roomconfig_moderatedroom"); end); @@ -780,7 +756,6 @@ function room_mt:destroy(newjid, reason, password) if password then x:tag("password"):text(password):up(); end x:up(); self:clear(x); - self:set_persistent(false); module:fire_event("muc-room-destroyed", { room = self }); end @@ -1220,6 +1195,10 @@ local members_only = module:require "muc/members_only"; room_mt.get_members_only = members_only.get; room_mt.set_members_only = members_only.set; +local persistent = module:require "muc/persistent"; +room_mt.get_persistent = persistent.get; +room_mt.set_persistent = persistent.set; + local history = module:require "muc/history"; room_mt.send_history = history.send; room_mt.get_historylength = history.get_length; diff --git a/plugins/muc/persistent.lib.lua b/plugins/muc/persistent.lib.lua new file mode 100644 index 00000000..ccf6fffe --- /dev/null +++ b/plugins/muc/persistent.lib.lua @@ -0,0 +1,49 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local function get_persistent(room) + return room._data.persistent; +end + +local function set_persistent(room, persistent) + persistent = persistent and true or nil; + if get_persistent(room) == persistent then return false; end + room._data.persistent = persistent; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_persistentroom"; + type = "boolean"; + label = "Make Room Persistent?"; + value = get_persistent(event.room); + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_persistentroom"]; + if new ~= nil and set_persistent(event.room, new) then + event.status_codes["104"] = true; + end +end); + +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = get_persistent(event.room) and "muc_persistent" or "muc_temporary"}):up(); +end); + +module:hook("muc-room-destroyed", function(event) + set_persistent(event.room, false); +end); + +return { + get = get_persistent; + set = set_persistent; +}; -- cgit v1.2.3 From bc3b5d98d53223906060c78c2a5dd284a7f1fa03 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Tue, 15 Apr 2014 18:20:56 -0400 Subject: plugins/muc/muc.lib: Refactor subject logic; fix bug of mixed up subject/author --- plugins/muc/muc.lib.lua | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 87f3d69c..782d4fe2 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -297,7 +297,7 @@ function room_mt:get_disco_items(stanza) end function room_mt:get_subject() - return self._data['subject'], self._data['subject_from'] + return self._data.subject_from, self._data.subject; end local function create_subject_message(from, subject) return st.message({from = from; type = "groupchat"}) @@ -878,7 +878,25 @@ function room_mt:handle_owner_query_set_to_room(origin, stanza) end end +module:hook("muc-subject-change", function(event) + local room, stanza = event.room, event.stanza; + local occupant = room:get_occupant_by_real_jid(stanza.attr.from); + if occupant.role == "moderator" or + ( occupant.role == "participant" and room:get_changesubject() ) then -- and participant + local subject = stanza:get_child_text("subject"); + room:set_subject(occupant.nick, subject); + else + event.origin.send(st.error_reply(stanza, "auth", "forbidden")); + end + return true; +end); + function room_mt:handle_groupchat_to_room(origin, stanza) + -- Prosody has made the decision that messages with are exclusively subject changes + -- e.g. body will be ignored; even if the subject change was not allowed + if stanza:get_child("subject") then + return module:fire_event("muc-subject-change", {room = self, origin = origin, stanza = stanza}); + end local from = stanza.attr.from; local occupant = self:get_occupant_by_real_jid(from); if not occupant then -- not in room @@ -887,24 +905,11 @@ function room_mt:handle_groupchat_to_room(origin, stanza) elseif occupant.role == "visitor" then origin.send(st.error_reply(stanza, "auth", "forbidden")); return true; - else - local from = stanza.attr.from; - stanza.attr.from = occupant.nick; - local subject = stanza:get_child_text("subject"); - if subject then - if occupant.role == "moderator" or - ( self:get_changesubject() and occupant.role == "participant" ) then -- and participant - self:set_subject(occupant.nick, subject); - else - stanza.attr.from = from; - origin.send(st.error_reply(stanza, "auth", "forbidden")); - end - else - self:broadcast_message(stanza); - end - stanza.attr.from = from; - return true; end + stanza.attr.from = occupant.nick; + self:broadcast_message(stanza); + stanza.attr.from = from; + return true; end -- hack - some buggy clients send presence updates to the room rather than their nick -- cgit v1.2.3 From 68f64d281702713a8a342807b6b4b51616f3d40d Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 16 Apr 2014 13:54:51 -0400 Subject: plugins/muc: Move subject code to seperate module --- plugins/muc/muc.lib.lua | 68 ++++----------------------------- plugins/muc/subject.lib.lua | 93 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 61 deletions(-) create mode 100644 plugins/muc/subject.lib.lua (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 782d4fe2..26b2d335 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -296,28 +296,6 @@ function room_mt:get_disco_items(stanza) return reply; end -function room_mt:get_subject() - return self._data.subject_from, self._data.subject; -end -local function create_subject_message(from, subject) - return st.message({from = from; type = "groupchat"}) - :tag('subject'):text(subject):up(); -end -function room_mt:send_subject(to) - local msg = create_subject_message(self:get_subject()); - msg.attr.to = to; - self:route_stanza(msg); -end -function room_mt:set_subject(current_nick, subject) - if subject == "" then subject = nil; end - self._data['subject'] = subject; - self._data['subject_from'] = current_nick; - if self.save then self:save(); end - local msg = create_subject_message(current_nick, subject); - self:broadcast_message(msg); - return true; -end - function room_mt:handle_kickable(origin, stanza) local real_jid = stanza.attr.from; local occupant = self:get_occupant_by_real_jid(real_jid); @@ -362,16 +340,6 @@ end function room_mt:set_public(public) return self:set_hidden(not public); end -function room_mt:set_changesubject(changesubject) - changesubject = changesubject and true or nil; - if self._data.changesubject ~= changesubject then - self._data.changesubject = changesubject; - if self.save then self:save(true); end - end -end -function room_mt:get_changesubject() - return self._data.changesubject; -end -- Give the room creator owner affiliation module:hook("muc-room-pre-create", function(event) @@ -400,11 +368,6 @@ module:hook("muc-occupant-joined", function(event) end); end, 80); --- Send subject to joining user -module:hook("muc-occupant-joined", function(event) - event.room:send_subject(event.stanza.attr.from); -end, 20); - function room_mt:handle_presence_to_occupant(origin, stanza) local type = stanza.attr.type; if type == "error" then -- error, kick em out! @@ -666,14 +629,6 @@ module:hook("muc-config-form", function(event) value = not event.room:get_hidden() }); end); -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_changesubject', - type = 'boolean', - label = 'Allow Occupants to Change Subject?', - value = event.room:get_changesubject() - }); -end); module:hook("muc-config-form", function(event) table.insert(event.form, { name = 'muc#roomconfig_moderatedroom', @@ -729,9 +684,6 @@ end); module:hook("muc-config-submitted", function(event) event.update_option("public", "muc#roomconfig_publicroom"); end); -module:hook("muc-config-submitted", function(event) - event.update_option("changesubject", "muc#roomconfig_changesubject"); -end); -- Removes everyone from the room function room_mt:clear(x) @@ -878,19 +830,6 @@ function room_mt:handle_owner_query_set_to_room(origin, stanza) end end -module:hook("muc-subject-change", function(event) - local room, stanza = event.room, event.stanza; - local occupant = room:get_occupant_by_real_jid(stanza.attr.from); - if occupant.role == "moderator" or - ( occupant.role == "participant" and room:get_changesubject() ) then -- and participant - local subject = stanza:get_child_text("subject"); - room:set_subject(occupant.nick, subject); - else - event.origin.send(st.error_reply(stanza, "auth", "forbidden")); - end - return true; -end); - function room_mt:handle_groupchat_to_room(origin, stanza) -- Prosody has made the decision that messages with are exclusively subject changes -- e.g. body will be ignored; even if the subject change was not allowed @@ -1204,6 +1143,13 @@ local persistent = module:require "muc/persistent"; room_mt.get_persistent = persistent.get; room_mt.set_persistent = persistent.set; +local subject = module:require "muc/subject"; +room_mt.get_changesubject = subject.get_changesubject; +room_mt.set_changesubject = subject.set_changesubject; +room_mt.get_subject = subject.get; +room_mt.set_subject = subject.set; +room_mt.send_subject = subject.send; + local history = module:require "muc/history"; room_mt.send_history = history.send; room_mt.get_historylength = history.get_length; diff --git a/plugins/muc/subject.lib.lua b/plugins/muc/subject.lib.lua new file mode 100644 index 00000000..a42a18c5 --- /dev/null +++ b/plugins/muc/subject.lib.lua @@ -0,0 +1,93 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local st = require "util.stanza"; + +local function create_subject_message(from, subject) + return st.message({from = from; type = "groupchat"}) + :tag("subject"):text(subject):up(); +end + +local function get_changesubject(room) + return room._data.changesubject; +end + +local function set_changesubject(room, changesubject) + changesubject = changesubject and true or nil; + if get_changesubject(room) == changesubject then return false; end + room._data.changesubject = changesubject; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_changesubject"; + type = "boolean"; + label = "Allow Occupants to Change Subject?"; + value = get_changesubject(event.room); + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_changesubject"]; + if new ~= nil and set_changesubject(event.room, new) then + event.status_codes["104"] = true; + end +end); + +local function get_subject(room) + return room._data.subject_from, room._data.subject; +end + +local function send_subject(room, to) + local msg = create_subject_message(get_subject(room)); + msg.attr.to = to; + room:route_stanza(msg); +end + +local function set_subject(room, from, subject) + if subject == "" then subject = nil; end + local old_from, old_subject = get_subject(room); + if old_subject == subject and old_from == from then return false; end + room._data.subject_from = from; + room._data.subject = subject; + if room.save then room:save(); end + local msg = create_subject_message(from, subject); + room:broadcast_message(msg); + return true; +end + +-- Send subject to joining user +module:hook("muc-occupant-joined", function(event) + event.room:send_subject(event.stanza.attr.from); +end, 20); + +-- Role check for subject changes +module:hook("muc-subject-change", function(event) + local room, stanza = event.room, event.stanza; + local occupant = room:get_occupant_by_real_jid(stanza.attr.from); + if occupant.role == "moderator" or + ( occupant.role == "participant" and get_changesubject(room) ) then -- and participant + local subject = stanza:get_child_text("subject"); + set_subject(room, occupant.nick, subject); + return true; + else + event.origin.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + end +end); + +return { + get_changesubject = get_changesubject; + set_changesubject = set_changesubject; + get = get_subject; + set = set_subject; + send = send_subject; +}; -- cgit v1.2.3 From aa2da859d066615a8382a7179b7c1896e2211998 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 16 Apr 2014 14:16:14 -0400 Subject: plugins/muc: Move 'hidden' ('public') code to own file --- plugins/muc/hidden.lib.lua | 45 +++++++++++++++++++++++++++++++++++++++++++++ plugins/muc/muc.lib.lua | 40 ++++++++++------------------------------ 2 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 plugins/muc/hidden.lib.lua (limited to 'plugins') diff --git a/plugins/muc/hidden.lib.lua b/plugins/muc/hidden.lib.lua new file mode 100644 index 00000000..5ce43db8 --- /dev/null +++ b/plugins/muc/hidden.lib.lua @@ -0,0 +1,45 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local function get_hidden(room) + return room._data.hidden; +end + +local function set_hidden(room, hidden) + hidden = hidden and true or nil; + if get_hidden(room) == hidden then return false; end + room._data.hidden = hidden; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_publicroom"; + type = "boolean"; + label = "Make Room Publicly Searchable?"; + value = not get_hidden(event.room); + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_publicroom"]; + if new ~= nil and set_hidden(event.room, not new) then + event.status_codes["104"] = true; + end +end); + +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = get_hidden(event.room) and "muc_hidden" or "muc_public"}):up(); +end); + +return { + get = get_hidden; + set = set_hidden; +}; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 26b2d335..fde6e17b 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -280,9 +280,6 @@ end); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = event.room:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up(); end); -module:hook("muc-disco#info", function(event) - event.reply:tag("feature", {var = event.room:get_hidden() and "muc_hidden" or "muc_public"}):up(); -end); module:hook("muc-disco#info", function(event) local count = iterators.count(event.room:each_occupant()); table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }); @@ -324,22 +321,6 @@ end function room_mt:get_moderated() return self._data.moderated; end -function room_mt:set_hidden(hidden) - hidden = hidden and true or nil; - if self._data.hidden ~= hidden then - self._data.hidden = hidden; - if self.save then self:save(true); end - end -end -function room_mt:get_hidden() - return self._data.hidden; -end -function room_mt:get_public() - return not self:get_hidden(); -end -function room_mt:set_public(public) - return self:set_hidden(not public); -end -- Give the room creator owner affiliation module:hook("muc-room-pre-create", function(event) @@ -621,14 +602,6 @@ function room_mt:get_form_layout(actor) }); return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form; end -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_publicroom', - type = 'boolean', - label = 'Make Room Publicly Searchable?', - value = not event.room:get_hidden() - }); -end); module:hook("muc-config-form", function(event) table.insert(event.form, { name = 'muc#roomconfig_moderatedroom', @@ -681,9 +654,6 @@ end module:hook("muc-config-submitted", function(event) event.update_option("moderated", "muc#roomconfig_moderatedroom"); end); -module:hook("muc-config-submitted", function(event) - event.update_option("public", "muc#roomconfig_publicroom"); -end); -- Removes everyone from the room function room_mt:clear(x) @@ -1127,6 +1097,16 @@ local description = module:require "muc/description"; room_mt.get_description = description.get; room_mt.set_description = description.set; +local hidden = module:require "muc/hidden"; +room_mt.get_hidden = hidden.get; +room_mt.set_hidden = hidden.set; +function room_mt:get_public() + return not self:get_hidden(); +end +function room_mt:set_public(public) + return self:set_hidden(not public); +end + local password = module:require "muc/password"; room_mt.get_password = password.get; room_mt.set_password = password.set; -- cgit v1.2.3 From 7c55b4a2e2bf8564efa21b5bf6815107d44364bc Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 18 Apr 2014 12:19:04 -0400 Subject: plugins/muc: Move 'moderated' code to seperate file; changes default "muc-get-default-role" behaviour --- plugins/muc/moderated.lib.lua | 53 +++++++++++++++++++++++++++++++++++++++++++ plugins/muc/muc.lib.lua | 37 ++++-------------------------- 2 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 plugins/muc/moderated.lib.lua (limited to 'plugins') diff --git a/plugins/muc/moderated.lib.lua b/plugins/muc/moderated.lib.lua new file mode 100644 index 00000000..c375b7ad --- /dev/null +++ b/plugins/muc/moderated.lib.lua @@ -0,0 +1,53 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- Copyright (C) 2014 Daurnimator +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local function get_moderated(room) + return room._data.moderated; +end + +local function set_moderated(room, moderated) + moderated = moderated and true or nil; + if get_moderated(room) == moderated then return false; end + room._data.moderated = moderated; + if room.save then room:save(true); end + return true; +end + +module:hook("muc-disco#info", function(event) + event.reply:tag("feature", {var = get_moderated(event.room) and "muc_moderated" or "muc_unmoderated"}):up(); +end); + +module:hook("muc-config-form", function(event) + table.insert(event.form, { + name = "muc#roomconfig_moderatedroom"; + type = "boolean"; + label = "Make Room Moderated?"; + value = get_moderated(event.room); + }); +end); + +module:hook("muc-config-submitted", function(event) + local new = event.fields["muc#roomconfig_moderatedroom"]; + if new ~= nil and set_moderated(event.room, new) then + event.status_codes["104"] = true; + end +end); + +module:hook("muc-get-default-role", function(event) + if event.affiliation == nil then + if get_moderated(event.room) then + return "visitor" + end + end +end, 1); + +return { + get = get_moderated; + set = set_moderated; +}; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index fde6e17b..53f91b83 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -49,15 +49,10 @@ end module:hook("muc-get-default-role", function(event) if event.affiliation_rank >= valid_affiliations.admin then return "moderator"; - elseif event.affiliation_rank >= valid_affiliations.member then + elseif event.affiliation_rank >= valid_affiliations.none then return "participant"; end end); -module:hook("muc-get-default-role", function(event) - if not event.affiliation then - return event.room:get_moderated() and "visitor" or "participant"; - end -end, -1); --- Occupant functions function room_mt:new_occupant(bare_real_jid, nick) @@ -277,9 +272,6 @@ end module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = "http://jabber.org/protocol/muc"}):up(); end); -module:hook("muc-disco#info", function(event) - event.reply:tag("feature", {var = event.room:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up(); -end); module:hook("muc-disco#info", function(event) local count = iterators.count(event.room:each_occupant()); table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }); @@ -311,23 +303,11 @@ function room_mt:handle_kickable(origin, stanza) return true; end -function room_mt:set_moderated(moderated) - moderated = moderated and true or nil; - if self._data.moderated ~= moderated then - self._data.moderated = moderated; - if self.save then self:save(true); end - end -end -function room_mt:get_moderated() - return self._data.moderated; -end - -- Give the room creator owner affiliation module:hook("muc-room-pre-create", function(event) event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); end, -1); - -- check if user is banned module:hook("muc-occupant-pre-join", function(event) local room, stanza = event.room, event.stanza; @@ -602,14 +582,6 @@ function room_mt:get_form_layout(actor) }); return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form; end -module:hook("muc-config-form", function(event) - table.insert(event.form, { - name = 'muc#roomconfig_moderatedroom', - type = 'boolean', - label = 'Make Room Moderated?', - value = event.room:get_moderated() - }); -end); function room_mt:process_form(origin, stanza) local form = stanza.tags[1]:get_child("x", "jabber:x:data"); @@ -651,9 +623,6 @@ function room_mt:process_form(origin, stanza) end return true; end -module:hook("muc-config-submitted", function(event) - event.update_option("moderated", "muc#roomconfig_moderatedroom"); -end); -- Removes everyone from the room function room_mt:clear(x) @@ -1119,6 +1088,10 @@ local members_only = module:require "muc/members_only"; room_mt.get_members_only = members_only.get; room_mt.set_members_only = members_only.set; +local moderated = module:require "muc/moderated"; +room_mt.get_moderated = moderated.get; +room_mt.set_moderated = moderated.set; + local persistent = module:require "muc/persistent"; room_mt.get_persistent = persistent.get; room_mt.set_persistent = persistent.set; -- cgit v1.2.3 From 544685b84c9618705ba70bb0c1a2ebb5258d67b2 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 18 Apr 2014 12:19:33 -0400 Subject: plugins/muc/muc.lib: When user leaves; set their role to nil --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 53f91b83..61520354 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -405,6 +405,7 @@ function room_mt:handle_presence_to_occupant(origin, stanza) local dest_nick; if dest_occupant == nil then -- Session is leaving log("debug", "session %s is leaving occupant %s", real_jid, orig_occupant.nick); + orig_occupant.role = nil; orig_occupant:set_session(real_jid, stanza); else log("debug", "session %s is changing from occupant %s to %s", real_jid, orig_occupant.nick, dest_occupant.nick); -- cgit v1.2.3 From 70a87e5d5e5fe24252d03a5263243c19745ea72d Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 18 Apr 2014 12:20:07 -0400 Subject: plugins/muc/subject.lib: If subject is not set by an occupant, it should come from room jid itself --- plugins/muc/subject.lib.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/subject.lib.lua b/plugins/muc/subject.lib.lua index a42a18c5..44fc915c 100644 --- a/plugins/muc/subject.lib.lua +++ b/plugins/muc/subject.lib.lua @@ -43,7 +43,8 @@ module:hook("muc-config-submitted", function(event) end); local function get_subject(room) - return room._data.subject_from, room._data.subject; + -- a stanza from the room JID (or from the occupant JID of the entity that set the subject) + return room._data.subject_from or room.jid, room._data.subject; end local function send_subject(room, to) @@ -66,7 +67,7 @@ end -- Send subject to joining user module:hook("muc-occupant-joined", function(event) - event.room:send_subject(event.stanza.attr.from); + send_subject(event.room, event.stanza.attr.from); end, 20); -- Role check for subject changes -- cgit v1.2.3 From 126eb3e5b9ea0372a264ddff6b4be37bd2029529 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 21 Apr 2014 17:39:18 -0400 Subject: plugins/muc/muc.lib: Remove reversed conditionals when firing pre- events --- plugins/muc/muc.lib.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 61520354..2e48ade1 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -822,7 +822,7 @@ function room_mt:handle_mediated_invite(origin, stanza) if not invitee then origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); return true; - elseif not module:fire_event("muc-pre-invite", {room = self, origin = origin, stanza = stanza}) then + elseif module:fire_event("muc-pre-invite", {room = self, origin = origin, stanza = stanza}) then return true; end local invite = st.message({from = self.jid, to = invitee, id = stanza.attr.id}) @@ -863,7 +863,7 @@ function room_mt:handle_mediated_decline(origin, stanza) if not declinee then origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); return true; - elseif not module:fire_event("muc-pre-decline", {room = self, origin = origin, stanza = stanza}) then + elseif module:fire_event("muc-pre-decline", {room = self, origin = origin, stanza = stanza}) then return true; end local decline = st.message({from = self.jid, to = declinee, id = stanza.attr.id}) -- cgit v1.2.3 From 9592b3a68c66c5d2ea10ea04ce3f47041cf2921d Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 21 Apr 2014 17:49:57 -0400 Subject: plugins/muc/members_only.lib: Compare affiliations via rank; wrap some long lines --- plugins/muc/members_only.lib.lua | 48 ++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/members_only.lib.lua b/plugins/muc/members_only.lib.lua index 84a17699..b0999f0b 100644 --- a/plugins/muc/members_only.lib.lua +++ b/plugins/muc/members_only.lib.lua @@ -51,13 +51,16 @@ end); -- registration required for entering members-only room module:hook("muc-occupant-pre-join", function(event) - local room, stanza = event.room, event.stanza; - local affiliation = room:get_affiliation(stanza.attr.from); - if affiliation == nil and get_members_only(event.room) then - local reply = st.error_reply(stanza, "auth", "registration-required"):up(); - reply.tags[1].attr.code = "407"; - event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); - return true; + local room = event.room; + if get_members_only(room) then + local stanza = event.stanza; + local affiliation = room:get_affiliation(stanza.attr.from); + if valid_affiliations[affiliation or "none"] <= valid_affiliations.none then + local reply = st.error_reply(stanza, "auth", "registration-required"):up(); + reply.tags[1].attr.code = "407"; + event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); + return true; + end end end, -5); @@ -65,21 +68,32 @@ end, -5); -- if a member without privileges to edit the member list attempts to invite another user -- the service SHOULD return a error to the occupant module:hook("muc-pre-invite", function(event) - local room, stanza = event.room, event.stanza; - if get_members_only(room) and room:get_affiliation(stanza.attr.from) or "none" < valid_affiliations.admin then - event.origin.send(st.error_reply(stanza, "auth", "forbidden")); - return true; + local room = event.room; + if get_members_only(room) then + local stanza = event.stanza; + local affiliation = room:get_affiliation(stanza.attr.from); + if valid_affiliations[affiliation or "none"] < valid_affiliations.admin then + event.origin.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + end end end); -- When an invite is sent; add an affiliation for the invitee module:hook("muc-invite", function(event) - local room, stanza = event.room, event.stanza; - local invitee = stanza.attr.to; - if get_members_only(room) and not room:get_affiliation(invitee) then - local from = stanza:get_child("x", "http://jabber.org/protocol/muc#user"):get_child("invite").attr.from; - module:log("debug", "%s invited %s into members only room %s, granting membership", from, invitee, room.jid); - room:set_affiliation(from, invitee, "member", "Invited by " .. from); -- This might fail; ignore for now + local room = event.room; + if get_members_only(room) then + local stanza = event.stanza; + local invitee = stanza.attr.to; + local affiliation = room:get_affiliation(invitee); + if valid_affiliations[affiliation or "none"] <= valid_affiliations.none then + local from = stanza:get_child("x", "http://jabber.org/protocol/muc#user") + :get_child("invite").attr.from; + module:log("debug", "%s invited %s into members only room %s, granting membership", + from, invitee, room.jid); + -- This might fail; ignore for now + room:set_affiliation(from, invitee, "member", "Invited by " .. from); + end end end); -- cgit v1.2.3 From 09fa17a7efd4502c7fdf21ec2492946bb59239e3 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 21 Apr 2014 17:51:32 -0400 Subject: plugins/muc/muc.lib: Move sending of occupant list to joining user out of hook, and into main flow: It has to occur before publication of their status --- plugins/muc/history.lib.lua | 2 +- plugins/muc/muc.lib.lua | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'plugins') diff --git a/plugins/muc/history.lib.lua b/plugins/muc/history.lib.lua index 417d62a8..596f92da 100644 --- a/plugins/muc/history.lib.lua +++ b/plugins/muc/history.lib.lua @@ -131,7 +131,7 @@ end -- Send history on join module:hook("muc-occupant-joined", function(event) send_history(event.room, event.stanza); -end, 50); -- Between occupant list (80) and subject(20) +end, 50); -- Before subject(20) -- add to history module:hook("muc-broadcast-message", function(event) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 2e48ade1..6aca5c62 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -320,15 +320,6 @@ module:hook("muc-occupant-pre-join", function(event) end end, -10); --- Send occupant list to newly joined user -module:hook("muc-occupant-joined", function(event) - local real_jid = event.stanza.attr.from; - event.room:send_occupant_list(real_jid, function(nick, occupant) - -- Don't include self - return occupant:get_presence(real_jid) == nil; - end); -end, 80); - function room_mt:handle_presence_to_occupant(origin, stanza) local type = stanza.attr.type; if type == "error" then -- error, kick em out! @@ -457,6 +448,14 @@ function room_mt:handle_presence_to_occupant(origin, stanza) dest_x:tag("status", {code = "100"}):up(); end self:save_occupant(dest_occupant); + + if orig_occupant == nil and is_first_dest_session then + -- Send occupant list to newly joined user + self:send_occupant_list(real_jid, function(nick, occupant) + -- Don't include self + return occupant:get_presence(real_jid) == nil; + end) + end self:publicise_occupant_status(dest_occupant, dest_x); if orig_occupant ~= nil and orig_occupant ~= dest_occupant and not is_last_orig_session then -- If user is swapping and wasn't last original session -- cgit v1.2.3