/usr/lib/prosody/modules/mod_roster.lua is in prosody 0.9.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | -- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local st = require "util.stanza"
local jid_split = require "util.jid".split;
local jid_prep = require "util.jid".prep;
local t_concat = table.concat;
local tonumber = tonumber;
local pairs, ipairs = pairs, ipairs;
local rm_load_roster = require "core.rostermanager".load_roster;
local rm_remove_from_roster = require "core.rostermanager".remove_from_roster;
local rm_add_to_roster = require "core.rostermanager".add_to_roster;
local rm_roster_push = require "core.rostermanager".roster_push;
local core_post_stanza = prosody.core_post_stanza;
module:add_feature("jabber:iq:roster");
local rosterver_stream_feature = st.stanza("ver", {xmlns="urn:xmpp:features:rosterver"});
module:hook("stream-features", function(event)
local origin, features = event.origin, event.features;
if origin.username then
features:add_child(rosterver_stream_feature);
end
end);
module:hook("iq/self/jabber:iq:roster:query", function(event)
local session, stanza = event.origin, event.stanza;
if stanza.attr.type == "get" then
local roster = st.reply(stanza);
local client_ver = tonumber(stanza.tags[1].attr.ver);
local server_ver = tonumber(session.roster[false].version or 1);
if not (client_ver and server_ver) or client_ver ~= server_ver then
roster:query("jabber:iq:roster");
-- Client does not support versioning, or has stale roster
for jid, item in pairs(session.roster) do
if jid ~= "pending" and jid then
roster:tag("item", {
jid = jid,
subscription = item.subscription,
ask = item.ask,
name = item.name,
});
for group in pairs(item.groups) do
roster:tag("group"):text(group):up();
end
roster:up(); -- move out from item
end
end
roster.tags[1].attr.ver = server_ver;
end
session.send(roster);
session.interested = true; -- resource is interested in roster updates
else -- stanza.attr.type == "set"
local query = stanza.tags[1];
if #query.tags == 1 and query.tags[1].name == "item"
and query.tags[1].attr.xmlns == "jabber:iq:roster" and query.tags[1].attr.jid
-- Protection against overwriting roster.pending, until we move it
and query.tags[1].attr.jid ~= "pending" then
local item = query.tags[1];
local from_node, from_host = jid_split(stanza.attr.from);
local jid = jid_prep(item.attr.jid);
local node, host, resource = jid_split(jid);
if not resource and host then
if jid ~= from_node.."@"..from_host then
if item.attr.subscription == "remove" then
local roster = session.roster;
local r_item = roster[jid];
if r_item then
local to_bare = node and (node.."@"..host) or host; -- bare JID
if r_item.subscription == "both" or r_item.subscription == "from" or (roster.pending and roster.pending[jid]) then
core_post_stanza(session, st.presence({type="unsubscribed", from=session.full_jid, to=to_bare}));
end
if r_item.subscription == "both" or r_item.subscription == "to" or r_item.ask then
core_post_stanza(session, st.presence({type="unsubscribe", from=session.full_jid, to=to_bare}));
end
local success, err_type, err_cond, err_msg = rm_remove_from_roster(session, jid);
if success then
session.send(st.reply(stanza));
rm_roster_push(from_node, from_host, jid);
else
session.send(st.error_reply(stanza, err_type, err_cond, err_msg));
end
else
session.send(st.error_reply(stanza, "modify", "item-not-found"));
end
else
local r_item = {name = item.attr.name, groups = {}};
if r_item.name == "" then r_item.name = nil; end
if session.roster[jid] then
r_item.subscription = session.roster[jid].subscription;
r_item.ask = session.roster[jid].ask;
else
r_item.subscription = "none";
end
for _, child in ipairs(item) do
if child.name == "group" then
local text = t_concat(child);
if text and text ~= "" then
r_item.groups[text] = true;
end
end
end
local success, err_type, err_cond, err_msg = rm_add_to_roster(session, jid, r_item);
if success then
-- Ok, send success
session.send(st.reply(stanza));
-- and push change to all resources
rm_roster_push(from_node, from_host, jid);
else
-- Adding to roster failed
session.send(st.error_reply(stanza, err_type, err_cond, err_msg));
end
end
else
-- Trying to add self to roster
session.send(st.error_reply(stanza, "cancel", "not-allowed"));
end
else
-- Invalid JID added to roster
session.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME what's the correct error?
end
else
-- Roster set didn't include a single item, or its name wasn't 'item'
session.send(st.error_reply(stanza, "modify", "bad-request"));
end
end
return true;
end);
module:hook_global("user-deleted", function(event)
local username, host = event.username, event.host;
if host ~= module.host then return end
local bare = username .. "@" .. host;
local roster = rm_load_roster(username, host);
for jid, item in pairs(roster) do
if jid and jid ~= "pending" then
if item.subscription == "both" or item.subscription == "from" or (roster.pending and roster.pending[jid]) then
module:send(st.presence({type="unsubscribed", from=bare, to=jid}));
end
if item.subscription == "both" or item.subscription == "to" or item.ask then
module:send(st.presence({type="unsubscribe", from=bare, to=jid}));
end
end
end
end, 300);
|