mirror of
https://github.com/bjc/prosody.git
synced 2025-04-01 20:27:39 +03:00
315 lines
10 KiB
Lua
315 lines
10 KiB
Lua
-- 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 "prosody.util.stanza"
|
|
|
|
local jid_split = require "prosody.util.jid".split;
|
|
local jid_resource = require "prosody.util.jid".resource;
|
|
local jid_prep = require "prosody.util.jid".prep;
|
|
local tonumber = tonumber;
|
|
local pairs = pairs;
|
|
|
|
local rostermanager = require "prosody.core.rostermanager";
|
|
local rm_load_roster = rostermanager.load_roster;
|
|
local rm_remove_from_roster = rostermanager.remove_from_roster;
|
|
local rm_add_to_roster = rostermanager.add_to_roster;
|
|
local rm_roster_push = rostermanager.roster_push;
|
|
|
|
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 then
|
|
roster:tag("item", {
|
|
jid = jid,
|
|
subscription = item.subscription,
|
|
ask = item.ask,
|
|
name = item.name,
|
|
});
|
|
for group in pairs(item.groups) do
|
|
roster:text_tag("group", group);
|
|
end
|
|
roster:up(); -- move out from item
|
|
end
|
|
end
|
|
roster.tags[1].attr.ver = tostring(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 then
|
|
local item = query.tags[1];
|
|
local from_node, from_host = jid_split(stanza.attr.from);
|
|
local jid = jid_prep(item.attr.jid);
|
|
if jid and not jid_resource(jid) 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
|
|
module:fire_event("roster-item-removed", {
|
|
username = from_node, jid = jid, item = r_item, origin = session, roster = roster,
|
|
});
|
|
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 group in item:childtags("group") do
|
|
local text = group:get_text();
|
|
if text then
|
|
r_item.groups[text] = true;
|
|
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;
|
|
local origin = event.origin or prosody.hosts[host];
|
|
if host ~= module.host then return end
|
|
local roster = rm_load_roster(username, host);
|
|
for jid, item in pairs(roster) do
|
|
if jid then
|
|
module:fire_event("roster-item-removed", {
|
|
username = username, jid = jid, item = item, roster = roster, origin = origin,
|
|
});
|
|
else
|
|
for pending_jid in pairs(item.pending) do
|
|
module:fire_event("roster-item-removed", {
|
|
username = username, jid = pending_jid, roster = roster, origin = origin,
|
|
});
|
|
end
|
|
end
|
|
end
|
|
end, 300);
|
|
|
|
-- API/commands
|
|
|
|
-- Make a *one-way* subscription. User will see when contact is online,
|
|
-- contact will not see when user is online.
|
|
function subscribe(user_jid, contact_jid)
|
|
local user_username, user_host = jid_split(user_jid);
|
|
local contact_username, contact_host = jid_split(contact_jid);
|
|
|
|
-- Update user's roster to say subscription request is pending. Bare hosts (e.g. components) don't have rosters.
|
|
if user_username ~= nil then
|
|
rostermanager.set_contact_pending_out(user_username, user_host, contact_jid);
|
|
end
|
|
|
|
if prosody.hosts[contact_host] and prosody.hosts[contact_host].type == "local" then -- Sending to a local host?
|
|
-- Update contact's roster to say subscription request is pending...
|
|
rostermanager.set_contact_pending_in(contact_username, contact_host, user_jid);
|
|
-- Update contact's roster to say subscription request approved...
|
|
rostermanager.subscribed(contact_username, contact_host, user_jid);
|
|
-- Update user's roster to say subscription request approved. Bare hosts (e.g. components) don't have rosters.
|
|
if user_username ~= nil then
|
|
rostermanager.process_inbound_subscription_approval(user_username, user_host, contact_jid);
|
|
end
|
|
else
|
|
-- Send a subscription request
|
|
local sub_request = st.presence({ from = user_jid, to = contact_jid, type = "subscribe" });
|
|
module:send(sub_request);
|
|
end
|
|
|
|
return true;
|
|
end
|
|
|
|
-- Make a mutual subscription between jid1 and jid2. Each JID will see
|
|
-- when the other one is online.
|
|
function subscribe_both(jid1, jid2)
|
|
local ok1, err1 = subscribe(jid1, jid2);
|
|
local ok2, err2 = subscribe(jid2, jid1);
|
|
return ok1 and ok2, err1 or err2;
|
|
end
|
|
|
|
-- Unsubscribes user from contact (not contact from user, if subscribed).
|
|
function unsubscribe(user_jid, contact_jid)
|
|
local user_username, user_host = jid_split(user_jid);
|
|
local contact_username, contact_host = jid_split(contact_jid);
|
|
|
|
-- Update user's roster to say subscription is cancelled...
|
|
rostermanager.unsubscribe(user_username, user_host, contact_jid);
|
|
if prosody.hosts[contact_host] then -- Local host?
|
|
-- Update contact's roster to say subscription is cancelled...
|
|
rostermanager.unsubscribed(contact_username, contact_host, user_jid);
|
|
end
|
|
return true;
|
|
end
|
|
|
|
-- Cancel any subscription in either direction.
|
|
function unsubscribe_both(jid1, jid2)
|
|
local ok1 = unsubscribe(jid1, jid2);
|
|
local ok2 = unsubscribe(jid2, jid1);
|
|
return ok1 and ok2;
|
|
end
|
|
|
|
module:add_item("shell-command", {
|
|
section = "roster";
|
|
section_desc = "View and manage user rosters (contact lists)";
|
|
name = "show";
|
|
desc = "Show a user's current roster";
|
|
args = {
|
|
{ name = "jid", type = "string" };
|
|
{ name = "sub", type = "string" };
|
|
};
|
|
host_selector = "jid";
|
|
handler = function(self, jid, sub) --luacheck: ignore 212/self
|
|
local print = self.session.print;
|
|
local it = require "prosody.util.iterators";
|
|
|
|
local roster = assert(rm_load_roster(jid_split(jid)));
|
|
|
|
local function sort_func(a, b)
|
|
if type(a) == "string" and type(b) == "string" then
|
|
return a < b;
|
|
else
|
|
return a == false;
|
|
end
|
|
end
|
|
|
|
local count = 0;
|
|
if sub == "pending" then
|
|
local pending_subs = roster[false].pending or {};
|
|
for pending_jid in it.sorted_pairs(pending_subs) do
|
|
print(pending_jid);
|
|
end
|
|
else
|
|
for contact, item in it.sorted_pairs(roster, sort_func) do
|
|
if contact and (not sub or sub == item.subscription) then
|
|
count = count + 1;
|
|
print(contact, ("sub=%s\task=%s"):format(item.subscription or "none", item.ask or "none"));
|
|
end
|
|
end
|
|
end
|
|
|
|
return true, ("Showing %d entries"):format(count);
|
|
end;
|
|
});
|
|
|
|
module:add_item("shell-command", {
|
|
section = "roster";
|
|
section_desc = "View and manage user rosters (contact lists)";
|
|
name = "subscribe";
|
|
desc = "Subscribe a user to another JID";
|
|
args = {
|
|
{ name = "jid", type = "string" };
|
|
{ name = "contact", type = "string" };
|
|
};
|
|
host_selector = "jid";
|
|
handler = function(self, jid, contact) --luacheck: ignore 212/self
|
|
return subscribe(jid, contact);
|
|
end;
|
|
});
|
|
|
|
module:add_item("shell-command", {
|
|
section = "roster";
|
|
section_desc = "View and manage user rosters (contact lists)";
|
|
name = "subscribe_both";
|
|
desc = "Subscribe a user and a contact JID to each other";
|
|
args = {
|
|
{ name = "jid", type = "string" };
|
|
{ name = "contact", type = "string" };
|
|
};
|
|
host_selector = "jid";
|
|
handler = function(self, jid, contact) --luacheck: ignore 212/self
|
|
return subscribe_both(jid, contact);
|
|
end;
|
|
});
|
|
|
|
|
|
module:add_item("shell-command", {
|
|
section = "roster";
|
|
section_desc = "View and manage user rosters (contact lists)";
|
|
name = "unsubscribe";
|
|
desc = "Unsubscribe a user from another JID";
|
|
args = {
|
|
{ name = "jid", type = "string" };
|
|
{ name = "contact", type = "string" };
|
|
};
|
|
host_selector = "jid";
|
|
handler = function(self, jid, contact) --luacheck: ignore 212/self
|
|
return unsubscribe(jid, contact);
|
|
end;
|
|
});
|
|
|
|
module:add_item("shell-command", {
|
|
section = "roster";
|
|
section_desc = "View and manage user rosters (contact lists)";
|
|
name = "unsubscribe_both";
|
|
desc = "Unubscribe a user and a contact JID from each other";
|
|
args = {
|
|
{ name = "jid", type = "string" };
|
|
{ name = "contact", type = "string" };
|
|
};
|
|
host_selector = "jid";
|
|
handler = function(self, jid, contact) --luacheck: ignore 212/self
|
|
return unsubscribe_both(jid, contact);
|
|
end;
|
|
});
|
|
|