From c33e8699c02ea861669e607e6fe1725db4822f1f Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:11 +0500 Subject: MUC: Return on message and iq to non-existent rooms (thanks Maranda). --- plugins/muc/mod_muc.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 0fa172ee..7312157c 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -135,6 +135,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 -- cgit v1.2.3 From c204694247ccaeffb4842d260f5e1a5fe0b9fe1e Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:13 +0500 Subject: mod_component: For disconnected external components, if a name is specified in config, return it in disco#info replies. --- plugins/mod_component.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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")); -- cgit v1.2.3 From 333dd0e428ab1c7ff75cf6ceba1cc8117d490f17 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:16 +0500 Subject: mod_presence, rostermanager: Bring outbound subscription cancellation in line with RFC6121. --- core/rostermanager.lua | 14 ++++++-------- plugins/mod_presence.lua | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 13 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_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 -- cgit v1.2.3 From f1f8cf4bafc4e605c3b86d42897bd3f85b1a8080 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:19 +0500 Subject: MUC: Fix private IQ and message routing. - All private IQ results are now routed to the resource that send the original request - Private messages are now routed to all of the user's sessions - Much cleaner code --- plugins/muc/muc.lib.lua | 72 ++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index a40dc05f..dd7a07f1 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 -- cgit v1.2.3 From 35113f999fdc2ef2f91c0bb6d066763518ff4829 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:22 +0500 Subject: MUC: Send unavailable presence when the component or server is shutting down. --- plugins/muc/mod_muc.lua | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 7312157c..94d8263c 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -178,7 +178,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) @@ -194,3 +196,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); -- cgit v1.2.3 From 664243f884bced858443a583e779e45282af3179 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:25 +0500 Subject: MUC: Expose room metatable in the MUC lib. --- plugins/muc/muc.lib.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index dd7a07f1..6d37fbcc 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1149,4 +1149,6 @@ function _M.new_room(jid, config) }, room_mt); end +_M.room_mt = room_mt; + return _M; -- cgit v1.2.3 From 57b3f76efccad9e5bc7b5f89664d607d02a5b45c Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:30 +0500 Subject: MUC: Give host and server admins "owner" affiliation in all rooms. --- plugins/muc/mod_muc.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 94d8263c..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); -- cgit v1.2.3 From 2161f2e88c3b8b882fc30ec49609d4262d18e380 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 1 Aug 2012 01:36:34 +0500 Subject: util.datamanager: Don't use os.rename on non-POSIX. It doesn't overwrite exisitng files on Windows. --- util/datamanager.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/util/datamanager.lua b/util/datamanager.lua index 79be07b0..ecf90bab 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 = {}; -- cgit v1.2.3