1-----------------------------------------------------------
2-- mod_roster_command: Manage rosters through prosodyctl
3-- version 0.02
4-----------------------------------------------------------
5-- Copyright (C) 2011 Matthew Wild
6-- Copyright (C) 2011 Adam Nielsen
7--
8-- This project is MIT/X11 licensed. Please see the
9-- COPYING file in the source package for more information.
10-----------------------------------------------------------
11
12if module.host ~= "*" then
13	module:log("error", "Do not load this module in Prosody, for correct usage see: https://modules.prosody.im/mod_roster_command.html");
14	return;
15end
16
17
18-- Workaround for lack of util.startup...
19local prosody = _G.prosody;
20local hosts = prosody.hosts;
21prosody.bare_sessions = prosody.bare_sessions or {};
22_G.bare_sessions = _G.bare_sessions or prosody.bare_sessions;
23
24local usermanager = require "core.usermanager";
25local rostermanager = require "core.rostermanager";
26local storagemanager = require "core.storagemanager";
27local jid = require "util.jid";
28local warn = require"util.prosodyctl".show_warning;
29
30-- Make a *one-way* subscription. User will see when contact is online,
31-- contact will not see when user is online.
32function subscribe(user_jid, contact_jid)
33	local user_username, user_host = jid.split(user_jid);
34	local contact_username, contact_host = jid.split(contact_jid);
35	if not hosts[user_host] then
36		warn("The host '%s' is not configured for this server.", user_host);
37		return;
38	end
39	if hosts[user_host].users.name == "null" then
40		storagemanager.initialize_host(user_host);
41		usermanager.initialize_host(user_host);
42	end
43	-- Update user's roster to say subscription request is pending. Bare hosts (e.g. components) don't have rosters.
44	if user_username ~= nil then
45		rostermanager.set_contact_pending_out(user_username, user_host, contact_jid);
46	end
47	if hosts[contact_host] then
48		if contact_host ~= user_host and hosts[contact_host].users.name == "null" then
49			storagemanager.initialize_host(contact_host);
50			usermanager.initialize_host(contact_host);
51		end
52		-- Update contact's roster to say subscription request is pending...
53		rostermanager.set_contact_pending_in(contact_username, contact_host, user_jid);
54		-- Update contact's roster to say subscription request approved...
55		rostermanager.subscribed(contact_username, contact_host, user_jid);
56		-- Update user's roster to say subscription request approved. Bare hosts (e.g. components) don't have rosters.
57		if user_username ~= nil then
58			rostermanager.process_inbound_subscription_approval(user_username, user_host, contact_jid);
59		end
60	end
61end
62
63-- Make a mutual subscription between jid1 and jid2. Each JID will see
64-- when the other one is online.
65function subscribe_both(jid1, jid2)
66	subscribe(jid1, jid2);
67	subscribe(jid2, jid1);
68end
69
70-- Unsubscribes user from contact (not contact from user, if subscribed).
71function unsubscribe(user_jid, contact_jid)
72	local user_username, user_host = jid.split(user_jid);
73	local contact_username, contact_host = jid.split(contact_jid);
74	if not hosts[user_host] then
75		warn("The host '%s' is not configured for this server.", user_host);
76		return;
77	end
78	if hosts[user_host].users.name == "null" then
79		storagemanager.initialize_host(user_host);
80		usermanager.initialize_host(user_host);
81	end
82	-- Update user's roster to say subscription is cancelled...
83	rostermanager.unsubscribe(user_username, user_host, contact_jid);
84	if hosts[contact_host] then
85		if contact_host ~= user_host and hosts[contact_host].users.name == "null" then
86			storagemanager.initialize_host(contact_host);
87			usermanager.initialize_host(contact_host);
88		end
89		-- Update contact's roster to say subscription is cancelled...
90		rostermanager.unsubscribed(contact_username, contact_host, user_jid);
91	end
92end
93
94-- Cancel any subscription in either direction.
95function unsubscribe_both(jid1, jid2)
96	unsubscribe(jid1, jid2);
97	unsubscribe(jid2, jid1);
98end
99
100-- Set the name shown and group used in the contact list
101function rename(user_jid, contact_jid, contact_nick, contact_group)
102	local user_username, user_host = jid.split(user_jid);
103	if not hosts[user_host] then
104		warn("The host '%s' is not configured for this server.", user_host);
105		return;
106	end
107	if hosts[user_host].users.name == "null" then
108		storagemanager.initialize_host(user_host);
109		usermanager.initialize_host(user_host);
110	end
111
112	-- Load user's roster and find the contact
113	local roster = rostermanager.load_roster(user_username, user_host);
114	local item = roster[contact_jid];
115	if item then
116		if contact_nick then
117			item.name = contact_nick;
118		end
119		if contact_group then
120			item.groups = {}; -- Remove from all current groups
121			item.groups[contact_group] = true;
122		end
123		rostermanager.save_roster(user_username, user_host, roster);
124	end
125end
126
127function remove(user_jid, contact_jid)
128	unsubscribe_both(user_jid, contact_jid);
129	local user_username, user_host = jid.split(user_jid);
130	local roster = rostermanager.load_roster(user_username, user_host);
131	roster[contact_jid] = nil;
132	rostermanager.save_roster(user_username, user_host, roster);
133end
134
135function module.command(arg)
136	local command = arg[1];
137	if not command then
138		warn("Valid subcommands: (un)subscribe(_both) | rename");
139		return 0;
140	end
141	table.remove(arg, 1);
142	if command == "subscribe" then
143		subscribe(arg[1], arg[2]);
144		return 0;
145	elseif command == "subscribe_both" then
146		subscribe_both(arg[1], arg[2]);
147		return 0;
148	elseif command == "unsubscribe" then
149		unsubscribe(arg[1], arg[2]);
150		return 0;
151	elseif command == "unsubscribe_both" then
152		unsubscribe_both(arg[1], arg[2]);
153		return 0;
154	elseif command == "remove" then
155		remove(arg[1], arg[2]);
156		return 0;
157	elseif command == "rename" then
158		rename(arg[1], arg[2], arg[3], arg[4]);
159		return 0;
160	else
161		warn("Unknown command: %s", command);
162		return 1;
163	end
164	return 0;
165end
166