diff options
-rw-r--r-- | core/rostermanager.lua | 14 | ||||
-rw-r--r-- | plugins/mod_component.lua | 12 | ||||
-rw-r--r-- | plugins/mod_presence.lua | 17 | ||||
-rw-r--r-- | plugins/muc/mod_muc.lua | 45 | ||||
-rw-r--r-- | plugins/muc/muc.lib.lua | 74 | ||||
-rw-r--r-- | util/datamanager.lua | 12 |
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 = {}; |