X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=plugins%2Fmod_privacy.lua;h=31ace9f9df126eb25ddaf66d40226bb1788ea28d;hb=082e265af83ea25d8203d12114c6cf1b5cef810a;hp=ab1eb87062bd2b573110d32fb7ebbc9d9abb8014;hpb=332ebda71d7fa4771a8222664b3b8bf341109432;p=prosody.git diff --git a/plugins/mod_privacy.lua b/plugins/mod_privacy.lua index ab1eb870..31ace9f9 100644 --- a/plugins/mod_privacy.lua +++ b/plugins/mod_privacy.lua @@ -1,31 +1,23 @@ -- Prosody IM --- Copyright (C) 2008-2009 Matthew Wild --- Copyright (C) 2008-2009 Waqas Hussain +-- Copyright (C) 2009-2010 Matthew Wild +-- Copyright (C) 2009-2010 Waqas Hussain -- Copyright (C) 2009 Thilo Cestonaro -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- -local prosody = prosody; +module:add_feature("jabber:iq:privacy"); + local st = require "util.stanza"; -local datamanager = require "util.datamanager"; -local bare_sessions, full_sessions = bare_sessions, full_sessions; +local bare_sessions, full_sessions = prosody.bare_sessions, prosody.full_sessions; local util_Jid = require "util.jid"; local jid_bare = util_Jid.bare; -local jid_split = util_Jid.split; +local jid_split, jid_join = util_Jid.split, util_Jid.join; local load_roster = require "core.rostermanager".load_roster; local to_number = tonumber; -function findNamedList(privacy_lists, name) - if privacy_lists.lists then - for i=1,#privacy_lists.lists do - if privacy_lists.lists[i].name == name then - return i; - end - end - end -end +local privacy_storage = module:open_store(); function isListUsed(origin, name, privacy_lists) local user = bare_sessions[origin.username.."@"..origin.host]; @@ -53,62 +45,6 @@ function isAnotherSessionUsingDefaultList(origin) end end -function sendUnavailable(origin, to, from) ---[[ example unavailable presence stanza - - Logged out - -]]-- - local presence = st.presence({from=from, type="unavailable"}); - presence:tag("status"):text("Logged out"); - - local node, host = jid_bare(to); - local bare = node .. "@" .. host; - - local user = bare_sessions[bare]; - if user then - for resource, session in pairs(user.sessions) do - presence.attr.to = session.full_jid; - module:log("debug", "send unavailable to: %s; from: %s", tostring(presence.attr.to), tostring(presence.attr.from)); - origin.send(presence); - end - end -end - -function sendNeededUnavailablePersences(origin, listnameOrItem) -- TODO implement it correctly! - if type(listnameOrItem) == "string" then - local listname = listnameOrItem; - for _,list in ipairs(privacy_lists.lists) do - if list.name == listname then - for _,item in ipairs(list.items) do - sendNeededUnavailablePersences(origin, item); - end - end - end - elseif type(listnameOrItem) == "table" then - module:log("debug", "got an item, check whether to send unavailable presence stanza or not"); - local item = listnameOrItem; - - if item["presence-out"] == true then - if item.type == "jid" then - sendUnavailable(origin, item.value, origin.full_jid); - elseif item.type == "group" then - elseif item.type == "subscription" then - elseif item.type == nil then - end - elseif item["presence-in"] == true then - if item.type == "jid" then - sendUnavailable(origin, origin.full_jid, item.value); - elseif item.type == "group" then - elseif item.type == "subscription" then - elseif item.type == nil then - end - end - else - module:log("debug", "got unknown type: %s", type(listnameOrItem)); - end -end - function declineList(privacy_lists, origin, stanza, which) if which == "default" then if isAnotherSessionUsingDefaultList(origin) then @@ -126,70 +62,56 @@ function declineList(privacy_lists, origin, stanza, which) end function activateList(privacy_lists, origin, stanza, which, name) - local idx = findNamedList(privacy_lists, name); + local list = privacy_lists.lists[name]; - if privacy_lists.default == nil then - privacy_lists.default = ""; - end - if origin.activePrivacyList == nil then - origin.activePrivacyList = ""; - end - - if which == "default" and idx ~= nil then + if which == "default" and list then if isAnotherSessionUsingDefaultList(origin) then return {"cancel", "conflict", "Another session is online and using the default list."}; end privacy_lists.default = name; origin.send(st.reply(stanza)); ---[[ - if origin.activePrivacyList == nil then - sendNeededUnavailablePersences(origin, name); - end -]]-- - elseif which == "active" and idx ~= nil then + elseif which == "active" and list then origin.activePrivacyList = name; origin.send(st.reply(stanza)); - -- sendNeededUnavailablePersences(origin, name); + elseif not list then + return {"cancel", "item-not-found", "No such list: "..name}; else - return {"modify", "bad-request", "Either not active or default given or unknown list name specified."}; + return {"modify", "bad-request", "No list chosen to be active or default."}; end return true; end function deleteList(privacy_lists, origin, stanza, name) - local idx = findNamedList(privacy_lists, name); + local list = privacy_lists.lists[name]; - if idx ~= nil then + if list then if isListUsed(origin, name, privacy_lists) then return {"cancel", "conflict", "Another session is online and using the list which should be deleted."}; end if privacy_lists.default == name then - privacy_lists.default = ""; + privacy_lists.default = nil; end if origin.activePrivacyList == name then - origin.activePrivacyList = ""; + origin.activePrivacyList = nil; end - table.remove(privacy_lists.lists, idx); + privacy_lists.lists[name] = nil; origin.send(st.reply(stanza)); return true; end return {"modify", "bad-request", "Not existing list specifed to be deleted."}; end -function createOrReplaceList (privacy_lists, origin, stanza, name, entries, roster) - local idx = findNamedList(privacy_lists, name); +function createOrReplaceList (privacy_lists, origin, stanza, name, entries) local bare_jid = origin.username.."@"..origin.host; if privacy_lists.lists == nil then privacy_lists.lists = {}; end - if idx == nil then - idx = #privacy_lists.lists + 1; - end + local list = {}; + privacy_lists.lists[name] = list; local orderCheck = {}; - local list = {}; list.name = name; list.items = {}; @@ -220,26 +142,7 @@ function createOrReplaceList (privacy_lists, origin, stanza, name, entries, rost end end - if tmp.type == "group" then - local found = false; - local roster = load_roster(origin.username, origin.host); - for jid,item in pairs(roster) do - if item.groups ~= nil then - for group in pairs(item.groups) do - if group == tmp.value then - found = true; - break; - end - end - if found == true then - break; - end - end - end - if found == false then - return {"cancel", "item-not-found", "Specifed roster group not existing."}; - end - elseif tmp.type == "subscription" then + if tmp.type == "subscription" then if tmp.value ~= "both" and tmp.value ~= "to" and tmp.value ~= "from" and @@ -251,20 +154,11 @@ function createOrReplaceList (privacy_lists, origin, stanza, name, entries, rost if tmp.action ~= "deny" and tmp.action ~= "allow" then return {"cancel", "bad-request", "Action must be either deny or allow."}; end - ---[[ - if (privacy_lists.default == name and origin.activePrivacyList == nil) or origin.activePrivacyList == name then - module:log("debug", "calling sendNeededUnavailablePresences!"); - -- item is valid and list is active, so send needed unavailable stanzas - sendNeededUnavailablePersences(origin, tmp); - end -]]-- list.items[#list.items + 1] = tmp; end table.sort(list, function(a, b) return a.order < b.order; end); - privacy_lists.lists[idx] = list; origin.send(st.reply(stanza)); if bare_sessions[bare_jid] ~= nil then local iq = st.iq ( { type = "set", id="push1" } ); @@ -286,17 +180,20 @@ function getList(privacy_lists, origin, stanza, name) reply:tag("query", {xmlns="jabber:iq:privacy"}); if name == nil then - reply:tag("active", {name=origin.activePrivacyList or ""}):up(); - reply:tag("default", {name=privacy_lists.default or ""}):up(); if privacy_lists.lists then - for _,list in ipairs(privacy_lists.lists) do - reply:tag("list", {name=list.name}):up(); + if origin.activePrivacyList then + reply:tag("active", {name=origin.activePrivacyList}):up(); + end + if privacy_lists.default then + reply:tag("default", {name=privacy_lists.default}):up(); + end + for name,list in pairs(privacy_lists.lists) do + reply:tag("list", {name=name}):up(); end end else - local idx = findNamedList(privacy_lists, name); - if idx ~= nil then - local list = privacy_lists.lists[idx]; + local list = privacy_lists.lists[name]; + if list then reply = reply:tag("list", {name=list.name}); for _,item in ipairs(list.items) do reply:tag("item", {type=item.type, value=item.value, action=item.action, order=item.order}); @@ -321,7 +218,16 @@ module:hook("iq/bare/jabber:iq:privacy:query", function(data) if stanza.attr.to == nil then -- only service requests to own bare JID local query = stanza.tags[1]; -- the query element local valid = false; - local privacy_lists = datamanager.load(origin.username, origin.host, "privacy") or {}; + local privacy_lists = privacy_storage:get(origin.username) or { lists = {} }; + + if privacy_lists.lists[1] then -- Code to migrate from old privacy lists format, remove in 0.8 + module:log("info", "Upgrading format of stored privacy lists for %s@%s", origin.username, origin.host); + local lists = privacy_lists.lists; + for idx, list in ipairs(lists) do + lists[list.name] = list; + lists[idx] = nil; + end + end if stanza.attr.type == "set" then if #query.tags == 1 then -- the element MUST NOT include more than one child element @@ -358,15 +264,16 @@ module:hook("iq/bare/jabber:iq:privacy:query", function(data) end if valid ~= true then - if valid[0] == nil then - valid[0] = "cancel"; - end + valid = valid or { "cancel", "bad-request", "Couldn't understand request" }; if valid[1] == nil then - valid[1] = "bad-request"; + valid[1] = "cancel"; + end + if valid[2] == nil then + valid[2] = "bad-request"; end - origin.send(st.error_reply(stanza, valid[0], valid[1], valid[2])); + origin.send(st.error_reply(stanza, valid[1], valid[2], valid[3])); else - datamanager.store(origin.username, origin.host, "privacy", privacy_lists); + privacy_storage:set(origin.username, privacy_lists); end return true; end @@ -374,42 +281,33 @@ end); function checkIfNeedToBeBlocked(e, session) local origin, stanza = e.origin, e.stanza; - local privacy_lists = datamanager.load(session.username, session.host, "privacy") or {}; + local privacy_lists = privacy_storage:get(session.username) or {}; local bare_jid = session.username.."@"..session.host; - local to = stanza.attr.to; + local to = stanza.attr.to or bare_jid; local from = stanza.attr.from; - local to_user = bare_jid == jid_bare(to); - local from_user = bare_jid == jid_bare(from); + local is_to_user = bare_jid == jid_bare(to); + local is_from_user = bare_jid == jid_bare(from); - module:log("debug", "stanza: %s, to: %s, from: %s", tostring(stanza.name), tostring(to), tostring(from)); + --module:log("debug", "stanza: %s, to: %s, from: %s", tostring(stanza.name), tostring(to), tostring(from)); if privacy_lists.lists == nil or - (session.activePrivacyList == nil or session.activePrivacyList == "") and - (privacy_lists.default == nil or privacy_lists.default == "") + not (session.activePrivacyList or privacy_lists.default) then return; -- Nothing to block, default is Allow all end - if from_user and to_user then - module:log("debug", "Not blocking communications between user's resources"); + if is_from_user and is_to_user then + --module:log("debug", "Not blocking communications between user's resources"); return; -- from one of a user's resource to another => HANDS OFF! end - local idx; - local list; - local item; local listname = session.activePrivacyList; - if listname == nil or listname == "" then + if listname == nil then listname = privacy_lists.default; -- no active list selected, use default list end - idx = findNamedList(privacy_lists, listname); - if idx == nil then - module:log("debug", "given privacy listname not found. name: %s", listname); - return; - end - list = privacy_lists.lists[idx]; - if list == nil then - module:log("debug", "privacy list index wrong. index: %d", idx); + local list = privacy_lists.lists[listname]; + if not list then -- should never happen + module:log("warn", "given privacy list not found. name: %s for user %s", listname, bare_jid); return; end for _,item in ipairs(list.items) do @@ -418,8 +316,8 @@ function checkIfNeedToBeBlocked(e, session) if ( (stanza.name == "message" and item.message) or (stanza.name == "iq" and item.iq) or - (stanza.name == "presence" and to_user and item["presence-in"]) or - (stanza.name == "presence" and from_user and item["presence-out"]) or + (stanza.name == "presence" and is_to_user and item["presence-in"]) or + (stanza.name == "presence" and is_from_user and item["presence-out"]) or (item.message == false and item.iq == false and item["presence-in"] == false and item["presence-out"] == false) ) then apply = true; @@ -427,11 +325,11 @@ function checkIfNeedToBeBlocked(e, session) if apply then local evilJid = {}; apply = false; - if to_user then - module:log("debug", "evil jid is (from): %s", from); + if is_to_user then + --module:log("debug", "evil jid is (from): %s", from); evilJid.node, evilJid.host, evilJid.resource = jid_split(from); else - module:log("debug", "evil jid is (to): %s", to); + --module:log("debug", "evil jid is (to): %s", to); evilJid.node, evilJid.host, evilJid.resource = jid_split(to); end if item.type == "jid" and @@ -443,17 +341,22 @@ function checkIfNeedToBeBlocked(e, session) block = (item.action == "deny"); elseif item.type == "group" then local roster = load_roster(session.username, session.host); - local groups = roster[evilJid.node .. "@" .. evilJid.host].groups; - for group in pairs(groups) do - if group == item.value then - apply = true; - block = (item.action == "deny"); - break; + local roster_entry = roster[jid_join(evilJid.node, evilJid.host)]; + if roster_entry then + local groups = roster_entry.groups; + for group in pairs(groups) do + if group == item.value then + apply = true; + block = (item.action == "deny"); + break; + end end end - elseif item.type == "subscription" and evilJid.node ~= nil and evilJid.host ~= nil then -- we need a valid bare evil jid + elseif item.type == "subscription" then -- we need a valid bare evil jid local roster = load_roster(session.username, session.host); - if roster[evilJid.node .. "@" .. evilJid.host].subscription == item.value then + local roster_entry = roster[jid_join(evilJid.node, evilJid.host)]; + if (not(roster_entry) and item.value == "none") + or (roster_entry and roster_entry.subscription == item.value) then apply = true; block = (item.action == "deny"); end @@ -464,6 +367,10 @@ function checkIfNeedToBeBlocked(e, session) end if apply then if block then + -- drop and not bounce groupchat messages, otherwise users will get kicked + if stanza.attr.type == "groupchat" then + return true; + end module:log("debug", "stanza blocked: %s, to: %s, from: %s", tostring(stanza.name), tostring(to), tostring(from)); if stanza.name == "message" then origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); @@ -472,7 +379,7 @@ function checkIfNeedToBeBlocked(e, session) end return true; -- stanza blocked ! else - module:log("debug", "stanza explicitly allowed!") + --module:log("debug", "stanza explicitly allowed!") return; end end @@ -488,7 +395,6 @@ function preCheckIncoming(e) end if resource == nil then local prio = 0; - local session_; if bare_sessions[node.."@"..host] ~= nil then for resource, session_ in pairs(bare_sessions[node.."@"..host].sessions) do if session_.priority ~= nil and session_.priority > prio then @@ -503,7 +409,7 @@ function preCheckIncoming(e) if session ~= nil then return checkIfNeedToBeBlocked(e, session); else - module:log("debug", "preCheckIncoming: Couldn't get session for jid: %s@%s/%s", tostring(node), tostring(host), tostring(resource)); + --module:log("debug", "preCheckIncoming: Couldn't get session for jid: %s@%s/%s", tostring(node), tostring(host), tostring(resource)); end end end @@ -516,7 +422,9 @@ function preCheckOutgoing(e) e.stanza.attr.from = e.stanza.attr.from .. "/" .. session.resource; end end - return checkIfNeedToBeBlocked(e, session); + if session.username then -- FIXME do properly + return checkIfNeedToBeBlocked(e, session); + end end module:hook("pre-message/full", preCheckOutgoing, 500);