comparison mod_admin_web/admin_web/mod_admin_web.lua @ 317:4f78f5020aa9

mod_admin_web: Get rid of the mod_pubsub dependency
author Florian Zeitz <florob@babelmonkeys.de>
date Fri, 14 Jan 2011 01:54:39 +0100
parents 5ec9125575fc
children ba2e78661ea8
comparison
equal deleted inserted replaced
316:c86fc337d56f 317:4f78f5020aa9
13 -- <encrypted/> 13 -- <encrypted/>
14 -- <compressed/> 14 -- <compressed/>
15 -- <in/> / <out/> 15 -- <in/> / <out/>
16 -- </session> 16 -- </session>
17 17
18 local stanza = require "util.stanza"; 18 local st = require "util.stanza";
19 local uuid_generate = require "util.uuid".generate; 19 local uuid_generate = require "util.uuid".generate;
20 local is_admin = require "usermanager".is_admin;
21 local pubsub = require "util.pubsub";
20 local httpserver = require "net.httpserver"; 22 local httpserver = require "net.httpserver";
23 local jid_bare = require "util.jid".bare;
21 local lfs = require "lfs"; 24 local lfs = require "lfs";
22 local open = io.open; 25 local open = io.open;
23 local stat = lfs.attributes; 26 local stat = lfs.attributes;
24 27
25 local host = module:get_host(); 28 local host = module:get_host();
26 local service = config.get("*", "core", "webadmin_pubsub_host") or ("pubsub." .. host); 29 local service;
27 30
28 local http_base = (prosody.paths.plugins or "./plugins/") .. "admin_web/www_files"; 31 local http_base = (prosody.paths.plugins or "./plugins/") .. "admin_web/www_files";
29 32
33 local xmlns_adminsub = "http://prosody.im/adminsub";
30 local xmlns_c2s_session = "http://prosody.im/streams/c2s"; 34 local xmlns_c2s_session = "http://prosody.im/streams/c2s";
31 local xmlns_s2s_session = "http://prosody.im/streams/s2s"; 35 local xmlns_s2s_session = "http://prosody.im/streams/s2s";
32 36
33 local response_301 = { status = "301 Moved Permanently" }; 37 local response_301 = { status = "301 Moved Permanently" };
34 local response_400 = { status = "400 Bad Request", body = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(" }; 38 local response_400 = { status = "400 Bad Request", body = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(" };
49 local id = idmap[name]; 53 local id = idmap[name];
50 if not id then 54 if not id then
51 id = uuid_generate(); 55 id = uuid_generate();
52 idmap[name] = id; 56 idmap[name] = id;
53 end 57 end
54 local item = stanza.stanza("item", { id = id }):tag("session", {xmlns = xmlns_c2s_session, jid = name}):up(); 58 local item = st.stanza("item", { id = id }):tag("session", {xmlns = xmlns_c2s_session, jid = name}):up();
55 if session.secure then 59 if session.secure then
56 item:tag("encrypted"):up(); 60 item:tag("encrypted"):up();
57 end 61 end
58 if session.compressed then 62 if session.compressed then
59 item:tag("compressed"):up(); 63 item:tag("compressed"):up();
60 end 64 end
61 hosts[service].modules.pubsub.service:publish(xmlns_c2s_session, service, id, item); 65 service:publish(xmlns_c2s_session, host, id, item);
62 module:log("debug", "Added client " .. name); 66 module:log("debug", "Added client " .. name);
63 end 67 end
64 68
65 function del_client(session) 69 function del_client(session)
66 local name = session.full_jid; 70 local name = session.full_jid;
67 local id = idmap[name]; 71 local id = idmap[name];
68 if id then 72 if id then
69 local notifier = stanza.stanza("retract", { id = id }); 73 local notifier = st.stanza("retract", { id = id });
70 hosts[service].modules.pubsub.service:retract(xmlns_c2s_session, service, id, notifier); 74 service:retract(xmlns_c2s_session, host, id, notifier);
71 end 75 end
72 end 76 end
73 77
74 function add_host(session, type) 78 function add_host(session, type)
75 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host); 79 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host);
76 local id = idmap[name.."_"..type]; 80 local id = idmap[name.."_"..type];
77 if not id then 81 if not id then
78 id = uuid_generate(); 82 id = uuid_generate();
79 idmap[name.."_"..type] = id; 83 idmap[name.."_"..type] = id;
80 end 84 end
81 local item = stanza.stanza("item", { id = id }):tag("session", {xmlns = xmlns_s2s_session, jid = name}) 85 local item = st.stanza("item", { id = id }):tag("session", {xmlns = xmlns_s2s_session, jid = name})
82 :tag(type):up(); 86 :tag(type):up();
83 if session.secure then 87 if session.secure then
84 item:tag("encrypted"):up(); 88 item:tag("encrypted"):up();
85 end 89 end
86 if session.compressed then 90 if session.compressed then
87 item:tag("compressed"):up(); 91 item:tag("compressed"):up();
88 end 92 end
89 hosts[service].modules.pubsub.service:publish(xmlns_s2s_session, service, id, item); 93 service:publish(xmlns_s2s_session, host, id, item);
90 module:log("debug", "Added host " .. name .. " s2s" .. type); 94 module:log("debug", "Added host " .. name .. " s2s" .. type);
91 end 95 end
92 96
93 function del_host(session, type) 97 function del_host(session, type)
94 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host); 98 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host);
95 local id = idmap[name.."_"..type]; 99 local id = idmap[name.."_"..type];
96 if id then 100 if id then
97 local notifier = stanza.stanza("retract", { id = id }); 101 local notifier = st.stanza("retract", { id = id });
98 hosts[service].modules.pubsub.service:retract(xmlns_s2s_session, service, id, notifier); 102 service:retract(xmlns_s2s_session, host, id, notifier);
99 end 103 end
100 end 104 end
101 105
102 local function preprocess_path(path) 106 local function preprocess_path(path)
103 if path:sub(1,1) ~= "/" then 107 if path:sub(1,1) ~= "/" then
131 return response_403; 135 return response_403;
132 end 136 end
133 local f, err = open(full_path, "rb"); 137 local f, err = open(full_path, "rb");
134 if not f then return response_404; end 138 if not f then return response_404; end
135 local data = f:read("*a"); 139 local data = f:read("*a");
136 data = data:gsub("%%PUBSUBHOST%%", service); 140 data = data:gsub("%%ADMINSUBHOST%%", host);
137 f:close(); 141 f:close();
138 if not data then 142 if not data then
139 return response_403; 143 return response_403;
140 end 144 end
141 local ext = path:match("%.([^.]*)$"); 145 local ext = path:match("%.([^.]*)$");
159 httpserver.new_from_config(http_conf, handle_file_request, { base = "admin" }); 163 httpserver.new_from_config(http_conf, handle_file_request, { base = "admin" });
160 end 164 end
161 165
162 prosody.events.add_handler("server-started", function () 166 prosody.events.add_handler("server-started", function ()
163 local host_session = prosody.hosts[host]; 167 local host_session = prosody.hosts[host];
164 if not select(2, hosts[service].modules.pubsub.service:get_nodes(service))[xmlns_s2s_session] then 168 if not select(2, service:get_nodes(true))[xmlns_s2s_session] then
165 local ok, errmsg = hosts[service].modules.pubsub.service:create(xmlns_s2s_session, service); 169 local ok, errmsg = service:create(xmlns_s2s_session, true);
166 if not ok then 170 if not ok then
167 module:log("warn", "Could not create node " .. xmlns_s2s_session .. ": " .. tostring(errmsg)); 171 module:log("warn", "Could not create node " .. xmlns_s2s_session .. ": " .. tostring(errmsg));
172 else
173 service:set_affiliation(xmlns_s2s_session, true, host, "owner")
168 end 174 end
169 end 175 end
170 176
171 for remotehost, session in pairs(host_session.s2sout) do 177 for remotehost, session in pairs(host_session.s2sout) do
172 if session.type ~= "s2sout_unauthed" then 178 if session.type ~= "s2sout_unauthed" then
177 if session.to_host == host then 183 if session.to_host == host then
178 add_host(session, "in"); 184 add_host(session, "in");
179 end 185 end
180 end 186 end
181 187
182 if not select(2, hosts[service].modules.pubsub.service:get_nodes(service))[xmlns_c2s_session] then 188 if not select(2, service:get_nodes(true))[xmlns_c2s_session] then
183 local ok, errmsg = hosts[service].modules.pubsub.service:create(xmlns_c2s_session, service); 189 local ok, errmsg = service:create(xmlns_c2s_session, true);
184 if not ok then 190 if not ok then
185 module:log("warn", "Could not create node " .. xmlns_c2s_session .. ": " .. tostring(errmsg)); 191 module:log("warn", "Could not create node " .. xmlns_c2s_session .. ": " .. tostring(errmsg));
192 else
193 service:set_affiliation(xmlns_c2s_session, true, host, "owner")
186 end 194 end
187 end 195 end
188 196
189 for username, user in pairs(host_session.sessions or {}) do 197 for username, user in pairs(host_session.sessions or {}) do
190 for resource, session in pairs(user.sessions or {}) do 198 for resource, session in pairs(user.sessions or {}) do
191 add_client(session); 199 add_client(session);
192 end 200 end
193 end 201 end
194 end); 202 end);
195 203
204 function simple_broadcast(node, jids, item)
205 item = st.clone(item);
206 item.attr.xmlns = nil; -- Clear the pubsub namespace
207 local message = st.message({ from = module.host, type = "headline" })
208 :tag("event", { xmlns = xmlns_adminsub .. "#event" })
209 :tag("items", { node = node })
210 :add_child(item);
211 for jid in pairs(jids) do
212 module:log("debug", "Sending notification to %s", jid);
213 message.attr.to = jid;
214 core_post_stanza(hosts[host], message);
215 end
216 end
217
218 function get_affiliation(jid)
219 local bare_jid = jid_bare(jid);
220 if is_admin(bare_jid, host) then
221 return "member";
222 else
223 return "none";
224 end
225 end
226
227 module:hook("iq/host/http://prosody.im/adminsub:adminsub", function(event)
228 local origin, stanza = event.origin, event.stanza;
229 local adminsub = stanza.tags[1];
230 local action = adminsub.tags[1];
231 local reply;
232 if action.name == "subscribe" then
233 local ok, ret = service:add_subscription(action.attr.node, stanza.attr.from, stanza.attr.from);
234 if ok then
235 reply = st.reply(stanza)
236 :tag("adminsub", { xmlns = xmlns_adminsub });
237 else
238 reply = st.error_reply(stanza, "cancel", ret);
239 end
240 elseif action.name == "items" then
241 local node = action.attr.node;
242 local ok, ret = service:get_items(node, stanza.attr.from);
243 if not ok then
244 return origin.send(st.error_reply(stanza, "cancel", ret));
245 end
246
247 local data = st.stanza("items", { node = node });
248 for _, entry in pairs(ret) do
249 data:add_child(entry);
250 end
251 if data then
252 reply = st.reply(stanza)
253 :tag("adminsub", { xmlns = xmlns_adminsub })
254 :add_child(data);
255 else
256 reply = st.error_reply(stanza, "cancel", "item-not-found");
257 end
258 else
259 reply = st.error_reply(stanza, "feature-not-implemented");
260 end
261 return origin.send(reply);
262 end);
263
196 module:hook("resource-bind", function(event) 264 module:hook("resource-bind", function(event)
197 add_client(event.session); 265 add_client(event.session);
198 end); 266 end);
199 267
200 module:hook("resource-unbind", function(event) 268 module:hook("resource-unbind", function(event)
201 del_client(event.session); 269 del_client(event.session);
270 service:remove_subscription(xmlns_c2s_session, host, event.session.full_jid);
271 service:remove_subscription(xmlns_s2s_session, host, event.session.full_jid);
202 end); 272 end);
203 273
204 module:hook("s2sout-established", function(event) 274 module:hook("s2sout-established", function(event)
205 add_host(event.session, "out"); 275 add_host(event.session, "out");
206 end); 276 end);
214 end); 284 end);
215 285
216 module:hook("s2sin-destroyed", function(event) 286 module:hook("s2sin-destroyed", function(event)
217 del_host(event.session, "in"); 287 del_host(event.session, "in");
218 end); 288 end);
289
290 service = pubsub.new({
291 broadcaster = simple_broadcast;
292 normalize_jid = jid_bare;
293 get_affiliation = get_affiliation;
294 capabilities = {
295 member = {
296 create = false;
297 publish = false;
298 retract = false;
299 get_nodes = true;
300
301 subscribe = true;
302 unsubscribe = true;
303 get_subscription = true;
304 get_subscriptions = true;
305 get_items = true;
306
307 subscribe_other = false;
308 unsubscribe_other = false;
309 get_subscription_other = false;
310 get_subscriptions_other = false;
311
312 be_subscribed = true;
313 be_unsubscribed = true;
314
315 set_affiliation = false;
316 };
317
318 owner = {
319 create = true;
320 publish = true;
321 retract = true;
322 get_nodes = true;
323
324 subscribe = true;
325 unsubscribe = true;
326 get_subscription = true;
327 get_subscriptions = true;
328 get_items = true;
329
330 subscribe_other = true;
331 unsubscribe_other = true;
332 get_subscription_other = true;
333 get_subscriptions_other = true;
334
335 be_subscribed = true;
336 be_unsubscribed = true;
337
338 set_affiliation = true;
339 };
340 };
341 });
342