3 local function log(type, message)
4 mainlog(type, "rostermanager", message);
7 local setmetatable = setmetatable;
8 local format = string.format;
9 local loadfile, setfenv, pcall = loadfile, setfenv, pcall;
10 local pairs, ipairs = pairs, ipairs;
14 local datamanager = require "util.datamanager"
15 local st = require "util.stanza";
17 module "rostermanager"
19 function add_to_roster(session, jid, item)
20 if session.roster then
21 local old_item = session.roster[jid];
22 session.roster[jid] = item;
23 if save_roster(session.username, session.host) then
26 session.roster[jid] = old_item;
27 return nil, "wait", "internal-server-error", "Unable to save roster";
30 return nil, "auth", "not-authorized", "Session's roster not loaded";
34 function remove_from_roster(session, jid)
35 if session.roster then
36 local old_item = session.roster[jid];
37 session.roster[jid] = nil;
38 if save_roster(session.username, session.host) then
41 session.roster[jid] = old_item;
42 return nil, "wait", "internal-server-error", "Unable to save roster";
45 return nil, "auth", "not-authorized", "Session's roster not loaded";
49 function roster_push(username, host, jid)
50 if jid ~= "pending" and hosts[host] and hosts[host].sessions[username] and hosts[host].sessions[username].roster then
51 local item = hosts[host].sessions[username].roster[jid];
52 local stanza = st.iq({type="set"});
53 stanza:tag("query", {xmlns = "jabber:iq:roster"});
55 stanza:tag("item", {jid = jid, subscription = item.subscription, name = item.name, ask = item.ask});
56 for group in pairs(item.groups) do
57 stanza:tag("group"):text(group):up();
60 stanza:tag("item", {jid = jid, subscription = "remove"});
62 stanza:up(); -- move out from item
63 stanza:up(); -- move out from stanza
65 for _, session in pairs(hosts[host].sessions[username].sessions) do
66 if session.interested then
67 -- FIXME do we need to set stanza.attr.to?
74 function load_roster(username, host)
75 log("debug", "load_roster: asked for: "..username.."@"..host);
76 if hosts[host] and hosts[host].sessions[username] then
77 local roster = hosts[host].sessions[username].roster;
79 log("debug", "load_roster: loading for new user: "..username.."@"..host);
80 roster = datamanager.load(username, host, "roster") or {};
81 hosts[host].sessions[username].roster = roster;
85 -- Attempt to load roster for non-loaded user
86 log("debug", "load_roster: loading for offline user: "..username.."@"..host);
87 return datamanager.load(username, host, "roster") or {};
90 function save_roster(username, host)
91 log("debug", "save_roster: saving roster for "..username.."@"..host);
92 if hosts[host] and hosts[host].sessions[username] and hosts[host].sessions[username].roster then
93 return datamanager.store(username, host, "roster", hosts[host].sessions[username].roster);
98 function process_inbound_subscription_approval(username, host, jid)
99 local roster = load_roster(username, host);
100 local item = roster[jid];
101 if item and item.ask then
102 if item.subscription == "none" then
103 item.subscription = "to";
104 else -- subscription == from
105 item.subscription = "both";
108 return datamanager.store(username, host, "roster", roster);
112 function process_inbound_subscription_cancellation(username, host, jid)
113 local roster = load_roster(username, host);
114 local item = roster[jid];
116 if is_contact_pending_out(username, host, jid) then
121 if item.subscription == "to" then
122 item.subscription = "none";
124 elseif item.subscription == "both" then
125 item.subscription = "from";
130 return datamanager.store(username, host, "roster", roster);
134 function process_inbound_unsubscribe(username, host, jid)
135 local roster = load_roster(username, host);
136 local item = roster[jid];
138 if is_contact_pending_in(username, host, jid) then
139 roster.pending[jid] = nil; -- TODO maybe delete roster.pending if empty?
143 if item.subscription == "from" then
144 item.subscription = "none";
146 elseif item.subscription == "both" then
147 item.subscription = "to";
152 return datamanager.store(username, host, "roster", roster);
156 function is_contact_subscribed(username, host, jid)
157 local roster = load_roster(username, host);
158 local item = roster[jid];
159 return item and (item.subscription == "from" or item.subscription == "both");
162 function is_contact_pending_in(username, host, jid)
163 local roster = load_roster(username, host);
164 return roster.pending and roster.pending[jid];
166 function set_contact_pending_in(username, host, jid, pending)
167 local roster = load_roster(username, host);
168 local item = roster[jid];
169 if item and (item.subscription == "from" or item.subscription == "both") then
172 if not roster.pending then roster.pending = {}; end
173 roster.pending[jid] = true;
174 return datamanager.store(username, host, "roster", roster);
176 function is_contact_pending_out(username, host, jid)
177 local roster = load_roster(username, host);
178 local item = roster[jid];
179 return item and item.ask;
181 function set_contact_pending_out(username, host, jid) -- subscribe
182 local roster = load_roster(username, host);
183 local item = roster[jid];
184 if item and (item.ask or item.subscription == "to" or item.subscription == "both") then
188 item = {subscription = "none", groups = {}};
191 item.ask = "subscribe";
192 log("debug", "set_contact_pending_out: saving roster; set "..username.."@"..host..".roster["..jid.."].ask=subscribe");
193 return datamanager.store(username, host, "roster", roster);
195 function unsubscribe(username, host, jid)
196 local roster = load_roster(username, host);
197 local item = roster[jid];
198 if not item then return false; end
199 if (item.subscription == "from" or item.subscription == "none") and not item.ask then
203 if item.subscription == "both" then
204 item.subscription = "from";
205 elseif item.subscription == "to" then
206 item.subscription = "none";
208 return datamanager.store(username, host, "roster", roster);
210 function subscribed(username, host, jid)
211 if is_contact_pending_in(username, host, jid) then
212 local roster = load_roster(username, host);
213 local item = roster[jid];
214 if item.subscription == "none" then
215 item.subscription = "from";
216 else -- subscription == to
217 item.subscription = "both";
219 roster.pending[jid] = nil;
220 -- TODO maybe remove roster.pending if empty
221 return datamanager.store(username, host, "roster", roster);
222 end -- TODO else implement optional feature pre-approval (ask = subscribed)
224 function unsubscribed(username, host, jid)
225 local roster = load_roster(username, host);
226 local item = roster[jid];
227 local pending = is_contact_pending_in(username, host, jid);
229 if is_contact_pending_in(username, host, jid) then
230 roster.pending[jid] = nil; -- TODO maybe delete roster.pending if empty?
234 if item.subscription == "from" then
235 item.subscription = "none";
237 elseif item.subscription == both then
238 item.subscription = "to";
243 return datamanager.store(username, host, "roster", roster);
247 function process_outbound_subscription_request(username, host, jid)
248 local roster = load_roster(username, host);
249 local item = roster[jid];
250 if item and (item.subscription == "none" or item.subscription == "from") then
251 item.ask = "subscribe";
252 return datamanager.store(username, host, "roster", roster);
256 --[[function process_outbound_subscription_approval(username, host, jid)
257 local roster = load_roster(username, host);
258 local item = roster[jid];
259 if item and (item.subscription == "none" or item.subscription == "from" then
260 item.ask = "subscribe";
261 return datamanager.store(username, host, "roster", roster);