util.x509: Only collect commonNames that pass idna
[prosody.git] / core / rostermanager.lua
blobd551a1b16a43542a9fcc3500052b8e84f5cb6ef4
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 --
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8 -- luacheck: globals prosody.bare_sessions.?.roster
12 local log = require "util.logger".init("rostermanager");
14 local new_id = require "util.id".short;
15 local new_cache = require "util.cache".new;
17 local pairs = pairs;
18 local tostring = tostring;
19 local type = type;
21 local hosts = prosody.hosts;
22 local bare_sessions = prosody.bare_sessions;
24 local um_user_exists = require "core.usermanager".user_exists;
25 local st = require "util.stanza";
26 local storagemanager = require "core.storagemanager";
28 local _ENV = nil;
29 -- luacheck: std none
31 local save_roster; -- forward declaration
33 local function add_to_roster(session, jid, item)
34 if session.roster then
35 local old_item = session.roster[jid];
36 session.roster[jid] = item;
37 if save_roster(session.username, session.host, nil, jid) then
38 return true;
39 else
40 session.roster[jid] = old_item;
41 return nil, "wait", "internal-server-error", "Unable to save roster";
42 end
43 else
44 return nil, "auth", "not-authorized", "Session's roster not loaded";
45 end
46 end
48 local function remove_from_roster(session, jid)
49 if session.roster then
50 local old_item = session.roster[jid];
51 session.roster[jid] = nil;
52 if save_roster(session.username, session.host, nil, jid) then
53 return true;
54 else
55 session.roster[jid] = old_item;
56 return nil, "wait", "internal-server-error", "Unable to save roster";
57 end
58 else
59 return nil, "auth", "not-authorized", "Session's roster not loaded";
60 end
61 end
63 local function roster_push(username, host, jid)
64 local roster = jid and hosts[host] and hosts[host].sessions[username] and hosts[host].sessions[username].roster;
65 if roster then
66 local item = hosts[host].sessions[username].roster[jid];
67 local stanza = st.iq({type="set", id=new_id()});
68 stanza:tag("query", {xmlns = "jabber:iq:roster", ver = tostring(roster[false].version or "1") });
69 if item then
70 stanza:tag("item", {jid = jid, subscription = item.subscription, name = item.name, ask = item.ask});
71 for group in pairs(item.groups) do
72 stanza:tag("group"):text(group):up();
73 end
74 else
75 stanza:tag("item", {jid = jid, subscription = "remove"});
76 end
77 stanza:up(); -- move out from item
78 stanza:up(); -- move out from stanza
79 -- stanza ready
80 for _, session in pairs(hosts[host].sessions[username].sessions) do
81 if session.interested then
82 session.send(stanza);
83 end
84 end
85 end
86 end
88 local function roster_metadata(roster, err)
89 local metadata = roster[false];
90 if not metadata then
91 metadata = { broken = err or nil };
92 roster[false] = metadata;
93 end
94 if roster.pending and type(roster.pending.subscription) ~= "string" then
95 metadata.pending = roster.pending;
96 roster.pending = nil;
97 elseif not metadata.pending then
98 metadata.pending = {};
99 end
100 return metadata;
103 local function load_roster(username, host)
104 local jid = username.."@"..host;
105 log("debug", "load_roster: asked for: %s", jid);
106 local user = bare_sessions[jid];
107 local roster;
108 if user then
109 roster = user.roster;
110 if roster then return roster; end
111 log("debug", "load_roster: loading for new user: %s", jid);
112 else -- Attempt to load roster for non-loaded user
113 log("debug", "load_roster: loading for offline user: %s", jid);
115 local roster_cache = hosts[host] and hosts[host].roster_cache;
116 if not roster_cache then
117 if hosts[host] then
118 roster_cache = new_cache(1024);
119 hosts[host].roster_cache = roster_cache;
121 else
122 roster = roster_cache:get(jid);
123 if roster then
124 log("debug", "load_roster: cache hit");
125 roster_cache:set(jid, roster);
126 if user then user.roster = roster; end
127 return roster;
128 else
129 log("debug", "load_roster: cache miss, loading from storage");
132 local roster_store = storagemanager.open(host, "roster", "keyval");
133 local data, err = roster_store:get(username);
134 roster = data or {};
135 if user then user.roster = roster; end
136 local legacy_pending = roster.pending and type(roster.pending.subscription) ~= "string";
137 roster_metadata(roster, err);
138 if legacy_pending then
139 -- Due to map store use, we need to manually delete this entry
140 log("debug", "Removing legacy 'pending' entry");
141 if not save_roster(username, host, roster, "pending") then
142 log("warn", "Could not remove legacy 'pendig' entry");
145 if roster[jid] then
146 roster[jid] = nil;
147 log("debug", "Roster for %s had a self-contact, removing", jid);
148 if not save_roster(username, host, roster, jid) then
149 log("warn", "Could not remove self-contact from roster for %s", jid);
152 if not err then
153 hosts[host].events.fire_event("roster-load", { username = username, host = host, roster = roster });
155 if roster_cache and not user then
156 log("debug", "load_roster: caching loaded roster");
157 roster_cache:set(jid, roster);
159 return roster, err;
162 function save_roster(username, host, roster, jid)
163 if not um_user_exists(username, host) then
164 log("debug", "not saving roster for %s@%s: the user doesn't exist", username, host);
165 return nil;
168 log("debug", "save_roster: saving roster for %s@%s, (%s)", username, host, jid or "all contacts");
169 if not roster then
170 roster = hosts[host] and hosts[host].sessions[username] and hosts[host].sessions[username].roster;
171 --if not roster then
172 -- --roster = load_roster(username, host);
173 -- return true; -- roster unchanged, no reason to save
174 --end
176 if roster then
177 local metadata = roster_metadata(roster);
178 if metadata.version ~= true then
179 metadata.version = (metadata.version or 0) + 1;
181 if metadata.broken then return nil, "Not saving broken roster" end
182 if jid == nil then
183 local roster_store = storagemanager.open(host, "roster", "keyval");
184 return roster_store:set(username, roster);
185 else
186 local roster_store = storagemanager.open(host, "roster", "map");
187 return roster_store:set_keys(username, { [false] = metadata, [jid] = roster[jid] or roster_store.remove });
190 log("warn", "save_roster: user had no roster to save");
191 return nil;
194 local function process_inbound_subscription_approval(username, host, jid)
195 local roster = load_roster(username, host);
196 local item = roster[jid];
197 if item and item.ask then
198 if item.subscription == "none" then
199 item.subscription = "to";
200 else -- subscription == from
201 item.subscription = "both";
203 item.ask = nil;
204 return save_roster(username, host, roster, jid);
208 local is_contact_pending_out -- forward declaration
210 local function process_inbound_subscription_cancellation(username, host, jid)
211 local roster = load_roster(username, host);
212 local item = roster[jid];
213 local changed = nil;
214 if is_contact_pending_out(username, host, jid) then
215 item.ask = nil;
216 changed = true;
218 if item then
219 if item.subscription == "to" then
220 item.subscription = "none";
221 changed = true;
222 elseif item.subscription == "both" then
223 item.subscription = "from";
224 changed = true;
227 if changed then
228 return save_roster(username, host, roster, jid);
232 local is_contact_pending_in -- forward declaration
234 local function process_inbound_unsubscribe(username, host, jid)
235 local roster = load_roster(username, host);
236 local item = roster[jid];
237 local changed = nil;
238 if is_contact_pending_in(username, host, jid) then
239 roster[false].pending[jid] = nil;
240 changed = true;
242 if item then
243 if item.subscription == "from" then
244 item.subscription = "none";
245 changed = true;
246 elseif item.subscription == "both" then
247 item.subscription = "to";
248 changed = true;
251 if changed then
252 return save_roster(username, host, roster, jid);
256 local function _get_online_roster_subscription(jidA, jidB)
257 local user = bare_sessions[jidA];
258 local item = user and (user.roster[jidB] or { subscription = "none" });
259 return item and item.subscription;
261 local function is_contact_subscribed(username, host, jid)
263 local selfjid = username.."@"..host;
264 local user_subscription = _get_online_roster_subscription(selfjid, jid);
265 if user_subscription then return (user_subscription == "both" or user_subscription == "from"); end
266 local contact_subscription = _get_online_roster_subscription(jid, selfjid);
267 if contact_subscription then return (contact_subscription == "both" or contact_subscription == "to"); end
269 local roster, err = load_roster(username, host);
270 local item = roster[jid];
271 return item and (item.subscription == "from" or item.subscription == "both"), err;
273 local function is_user_subscribed(username, host, jid)
275 local selfjid = username.."@"..host;
276 local user_subscription = _get_online_roster_subscription(selfjid, jid);
277 if user_subscription then return (user_subscription == "both" or user_subscription == "to"); end
278 local contact_subscription = _get_online_roster_subscription(jid, selfjid);
279 if contact_subscription then return (contact_subscription == "both" or contact_subscription == "from"); end
281 local roster, err = load_roster(username, host);
282 local item = roster[jid];
283 return item and (item.subscription == "to" or item.subscription == "both"), err;
286 function is_contact_pending_in(username, host, jid)
287 local roster = load_roster(username, host);
288 return roster[false].pending[jid] ~= nil;
290 local function set_contact_pending_in(username, host, jid, stanza)
291 local roster = load_roster(username, host);
292 local item = roster[jid];
293 if item and (item.subscription == "from" or item.subscription == "both") then
294 return; -- false
296 roster[false].pending[jid] = st.is_stanza(stanza) and st.preserialize(stanza) or true;
297 return save_roster(username, host, roster, jid);
299 function is_contact_pending_out(username, host, jid)
300 local roster = load_roster(username, host);
301 local item = roster[jid];
302 return item and item.ask;
304 local function set_contact_pending_out(username, host, jid) -- subscribe
305 local roster = load_roster(username, host);
306 local item = roster[jid];
307 if item and (item.ask or item.subscription == "to" or item.subscription == "both") then
308 return true;
310 if not item then
311 item = {subscription = "none", groups = {}};
312 roster[jid] = item;
314 item.ask = "subscribe";
315 log("debug", "set_contact_pending_out: saving roster; set %s@%s.roster[%q].ask=subscribe", username, host, jid);
316 return save_roster(username, host, roster, jid);
318 local function unsubscribe(username, host, jid)
319 local roster = load_roster(username, host);
320 local item = roster[jid];
321 if not item then return false; end
322 if (item.subscription == "from" or item.subscription == "none") and not item.ask then
323 return true;
325 item.ask = nil;
326 if item.subscription == "both" then
327 item.subscription = "from";
328 elseif item.subscription == "to" then
329 item.subscription = "none";
331 return save_roster(username, host, roster, jid);
333 local function subscribed(username, host, jid)
334 if is_contact_pending_in(username, host, jid) then
335 local roster = load_roster(username, host);
336 local item = roster[jid];
337 if not item then -- FIXME should roster item be auto-created?
338 item = {subscription = "none", groups = {}};
339 roster[jid] = item;
341 if item.subscription == "none" then
342 item.subscription = "from";
343 else -- subscription == to
344 item.subscription = "both";
346 roster[false].pending[jid] = nil;
347 return save_roster(username, host, roster, jid);
348 end -- TODO else implement optional feature pre-approval (ask = subscribed)
350 local function unsubscribed(username, host, jid)
351 local roster = load_roster(username, host);
352 local item = roster[jid];
353 local pending = is_contact_pending_in(username, host, jid);
354 if pending then
355 roster[false].pending[jid] = nil;
357 local is_subscribed;
358 if item then
359 if item.subscription == "from" then
360 item.subscription = "none";
361 is_subscribed = true;
362 elseif item.subscription == "both" then
363 item.subscription = "to";
364 is_subscribed = true;
367 local success = (pending or is_subscribed) and save_roster(username, host, roster, jid);
368 return success, pending, is_subscribed;
371 local function process_outbound_subscription_request(username, host, jid)
372 local roster = load_roster(username, host);
373 local item = roster[jid];
374 if item and (item.subscription == "none" or item.subscription == "from") then
375 item.ask = "subscribe";
376 return save_roster(username, host, roster, jid);
380 --[[function process_outbound_subscription_approval(username, host, jid)
381 local roster = load_roster(username, host);
382 local item = roster[jid];
383 if item and (item.subscription == "none" or item.subscription == "from" then
384 item.ask = "subscribe";
385 return save_roster(username, host, roster);
387 end]]
391 return {
392 add_to_roster = add_to_roster;
393 remove_from_roster = remove_from_roster;
394 roster_push = roster_push;
395 load_roster = load_roster;
396 save_roster = save_roster;
397 process_inbound_subscription_approval = process_inbound_subscription_approval;
398 process_inbound_subscription_cancellation = process_inbound_subscription_cancellation;
399 process_inbound_unsubscribe = process_inbound_unsubscribe;
400 is_contact_subscribed = is_contact_subscribed;
401 is_user_subscribed = is_user_subscribed;
402 is_contact_pending_in = is_contact_pending_in;
403 set_contact_pending_in = set_contact_pending_in;
404 is_contact_pending_out = is_contact_pending_out;
405 set_contact_pending_out = set_contact_pending_out;
406 unsubscribe = unsubscribe;
407 subscribed = subscribed;
408 unsubscribed = unsubscribed;
409 process_outbound_subscription_request = process_outbound_subscription_request;