aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/rostermanager.lua14
-rw-r--r--plugins/mod_component.lua12
-rw-r--r--plugins/mod_presence.lua17
-rw-r--r--plugins/muc/mod_muc.lua45
-rw-r--r--plugins/muc/muc.lib.lua74
-rw-r--r--util/datamanager.lua12
6 files changed, 133 insertions, 41 deletions
diff --git a/core/rostermanager.lua b/core/rostermanager.lua
index ac4f4e3d..fdb890f9 100644
--- a/core/rostermanager.lua
+++ b/core/rostermanager.lua
@@ -278,23 +278,21 @@ function unsubscribed(username, host, jid)
local roster = load_roster(username, host);
local item = roster[jid];
local pending = is_contact_pending_in(username, host, jid);
- local changed = nil;
- if is_contact_pending_in(username, host, jid) then
+ if pending then
roster.pending[jid] = nil; -- TODO maybe delete roster.pending if empty?
- changed = true;
end
+ local subscribed;
if item then
if item.subscription == "from" then
item.subscription = "none";
- changed = true;
+ subscribed = true;
elseif item.subscription == "both" then
item.subscription = "to";
- changed = true;
+ subscribed = true;
end
end
- if changed then
- return save_roster(username, host, roster);
- end
+ local success = (pending or subscribed) and save_roster(username, host, roster);
+ return success, pending, subscribed;
end
function process_outbound_subscription_request(username, host, jid)
diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua
index 751de59b..cff54f61 100644
--- a/plugins/mod_component.lua
+++ b/plugins/mod_component.lua
@@ -93,6 +93,18 @@ function module.add_host(module)
stanza.attr.xmlns = nil;
send(stanza);
else
+ if stanza.name == "iq" and stanza.attr.type == "get" and stanza.attr.to == module.host then
+ local query = stanza.tags[1];
+ local node = query.attr.node;
+ if query.name == "query" and query.attr.xmlns == "http://jabber.org/protocol/disco#info" and (not node or node == "") then
+ local name = module:get_option_string("name");
+ if name then
+ event.origin.send(st.reply(stanza):tag("query", { xmlns = "http://jabber.org/protocol/disco#info" })
+ :tag("identity", { category = "component", type = "generic", name = module:get_option_string("name", "Prosody") }))
+ return true;
+ end
+ end
+ end
module:log("warn", "Component not connected, bouncing error for: %s", stanza:top_tag());
if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
event.origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable"));
diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua
index dac86ae6..077bc31f 100644
--- a/plugins/mod_presence.lua
+++ b/plugins/mod_presence.lua
@@ -198,12 +198,19 @@ function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_
core_post_stanza(origin, stanza);
send_presence_of_available_resources(node, host, to_bare, origin);
elseif stanza.attr.type == "unsubscribed" then
- -- 1. route stanza
- -- 2. roster push (subscription = none or to)
- if rostermanager.unsubscribed(node, host, to_bare) then
- rostermanager.roster_push(node, host, to_bare);
+ -- 1. send unavailable
+ -- 2. route stanza
+ -- 3. roster push (subscription = from or both)
+ local success, pending_in, subscribed = rostermanager.unsubscribed(node, host, to_bare);
+ if success then
+ if subscribed then
+ rostermanager.roster_push(node, host, to_bare);
+ end
+ core_post_stanza(origin, stanza);
+ if subscribed then
+ send_presence_of_available_resources(node, host, to_bare, origin, st.presence({ type = "unavailable" }));
+ end
end
- core_post_stanza(origin, stanza);
else
origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid presence type"));
end
diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua
index 0fa172ee..cfb1dd22 100644
--- a/plugins/muc/mod_muc.lua
+++ b/plugins/muc/mod_muc.lua
@@ -22,7 +22,8 @@ if restrict_room_creation then
restrict_room_creation = nil;
end
end
-local muc_new_room = module:require "muc".new_room;
+local muclib = module:require "muc";
+local muc_new_room = muclib.new_room;
local jid_split = require "util.jid".split;
local jid_bare = require "util.jid".bare;
local st = require "util.stanza";
@@ -42,6 +43,17 @@ local function is_admin(jid)
return um_is_admin(jid, module.host);
end
+local _set_affiliation = muc_new_room.room_mt.set_affiliation;
+local _get_affiliation = muc_new_room.room_mt.get_affiliation;
+function muclib.room_mt:get_affiliation(jid)
+ if is_admin(jid) then return "owner"; end
+ return _get_affiliation(self, jid);
+end
+function muclib.room_mt:set_affiliation(actor, jid, affiliation, callback, reason)
+ if is_admin(jid) then return nil, "modify", "not-acceptable";; end
+ return _set_affiliation(self, actor, jid, affiliation, callback, reason);
+end
+
local function room_route_stanza(room, stanza) module:send(stanza); end
local function room_save(room, forced)
local node = jid_split(room.jid);
@@ -135,6 +147,10 @@ function stanza_handler(event)
local bare = jid_bare(stanza.attr.to);
local room = rooms[bare];
if not room then
+ if stanza.name ~= "presence" then
+ origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
+ return true;
+ end
if not(restrict_room_creation) or
(restrict_room_creation == "admin" and is_admin(stanza.attr.from)) or
(restrict_room_creation == "local" and select(2, jid_split(stanza.attr.from)) == module.host:gsub("^[^%.]+%.", "")) then
@@ -174,7 +190,9 @@ end
hosts[module:get_host()].muc = { rooms = rooms };
+local saved = false;
module.save = function()
+ saved = true;
return {rooms = rooms};
end
module.restore = function(data)
@@ -190,3 +208,28 @@ module.restore = function(data)
end
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();
+ for roomjid, room in pairs(rooms) do
+ shutdown_room(room, stanza);
+ end
+ shutdown_room(host_room, stanza);
+ end
+end
+module.unload = shutdown_component;
+module:hook_global("server-stopping", shutdown_component);
diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua
index a40dc05f..6d37fbcc 100644
--- a/plugins/muc/muc.lib.lua
+++ b/plugins/muc/muc.lib.lua
@@ -347,6 +347,31 @@ function room_mt:set_historylength(length)
end
+local function construct_stanza_id(room, stanza)
+ local from_jid, to_nick = stanza.attr.from, stanza.attr.to;
+ local from_nick = room._jid_nick[from_jid];
+ local occupant = room._occupants[to_nick];
+ local to_jid = occupant.jid;
+
+ return from_nick, to_jid, base64.encode(to_jid.."\0"..stanza.attr.id.."\0"..md5(from_jid));
+end
+local function deconstruct_stanza_id(room, stanza)
+ local from_jid_possiblybare, to_nick = stanza.attr.from, stanza.attr.to;
+ local from_jid, id, to_jid_hash = (base64.decode(stanza.attr.id) or ""):match("^(.+)%z(.*)%z(.+)$");
+ local from_nick = room._jid_nick[from_jid];
+
+ if not(from_nick) then return; end
+ if not(from_jid_possiblybare == from_jid or from_jid_possiblybare == jid_bare(from_jid)) then return; end
+
+ local occupant = room._occupants[to_nick];
+ for to_jid in pairs(occupant and occupant.sessions or {}) do
+ if md5(to_jid) == to_jid_hash then
+ return from_nick, to_jid, id;
+ end
+ end
+end
+
+
function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc
local from, to = stanza.attr.from, stanza.attr.to;
local room = jid_bare(to);
@@ -497,24 +522,11 @@ function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc
end
end
elseif not current_nick then -- not in room
- if type == "error" or type == "result" then
- local id = stanza.name == "iq" and stanza.attr.id and base64.decode(stanza.attr.id);
- local _nick, _id, _hash = (id or ""):match("^(.+)%z(.*)%z(.+)$");
- local occupant = self._occupants[stanza.attr.to];
- if occupant and _nick and self._jid_nick[_nick] and _id and _hash then
- local id, _to = stanza.attr.id;
- for jid in pairs(occupant.sessions) do
- if md5(jid) == _hash then
- _to = jid;
- break;
- end
- end
- if _to then
- stanza.attr.to, stanza.attr.from, stanza.attr.id = _to, self._jid_nick[_nick], _id;
- self:_route_stanza(stanza);
- stanza.attr.to, stanza.attr.from, stanza.attr.id = to, from, id;
- end
- end
+ if type == "error" or type == "result" and stanza.name == "iq" then
+ local id = stanza.attr.id;
+ stanza.attr.from, stanza.attr.to, stanza.attr.id = deconstruct_stanza_id(self, stanza);
+ self:_route_stanza(stanza);
+ stanza.attr.from, stanza.attr.to, stanza.attr.id = from, to, id;
else
origin.send(st.error_reply(stanza, "cancel", "not-acceptable"));
end
@@ -527,16 +539,22 @@ function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc
local o_data = self._occupants[to];
if o_data then
log("debug", "%s sent private stanza to %s (%s)", from, to, o_data.jid);
- local jid = o_data.jid;
- local bare = jid_bare(jid);
- stanza.attr.to, stanza.attr.from = jid, current_nick;
- local id = stanza.attr.id;
- if stanza.name=='iq' and type=='get' and stanza.tags[1].attr.xmlns == 'vcard-temp' and bare ~= jid then
- stanza.attr.to = bare;
- stanza.attr.id = base64.encode(jid.."\0"..id.."\0"..md5(from));
+ if stanza.name == "iq" then
+ local id = stanza.attr.id;
+ stanza.attr.from, stanza.attr.to, stanza.attr.id = construct_stanza_id(self, stanza);
+ if type == 'get' and stanza.tags[1].attr.xmlns == 'vcard-temp' then
+ stanza.attr.to = jid_bare(stanza.attr.to);
+ end
+ self:_route_stanza(stanza);
+ stanza.attr.from, stanza.attr.to, stanza.attr.id = from, to, id;
+ else -- message
+ stanza.attr.from = current_nick;
+ for jid in pairs(o_data.sessions) do
+ stanza.attr.to = jid;
+ self:_route_stanza(stanza);
+ end
+ stanza.attr.from, stanza.attr.to = from, to;
end
- self:_route_stanza(stanza);
- stanza.attr.to, stanza.attr.from, stanza.attr.id = to, from, id;
elseif type ~= "error" and type ~= "result" then -- recipient not in room
origin.send(st.error_reply(stanza, "cancel", "item-not-found", "Recipient not in room"));
end
@@ -1131,4 +1149,6 @@ function _M.new_room(jid, config)
}, room_mt);
end
+_M.room_mt = room_mt;
+
return _M;
diff --git a/util/datamanager.lua b/util/datamanager.lua
index c8b905ac..9f7600ee 100644
--- a/util/datamanager.lua
+++ b/util/datamanager.lua
@@ -171,6 +171,18 @@ local function atomic_store(filename, data)
return nil, msg;
end
+if prosody.platform ~= "posix" then
+ -- os.rename does not overwrite existing files on Windows
+ -- TODO We could use Transactional NTFS on Vista and above
+ function atomic_store(filename, data)
+ local f, err = io_open(filename, "w");
+ if not f then return f, err; end
+ local ok, msg = f:write(data);
+ if not ok then f:close(); return ok, msg; end
+ return f:close();
+ end
+end
+
function store(username, host, datastore, data)
if not data then
data = {};