comparison mod_admin_web/admin_web/mod_admin_web_timber.lua @ 636:b3a3199255d7

mod_admin_web: Add timber version. Separate for now
author Florian Zeitz <florob@babelmonkeys.de>
date Fri, 27 Apr 2012 03:30:47 +0200
parents
children 210f4ce2697c
comparison
equal deleted inserted replaced
635:30be50d2537f 636:b3a3199255d7
1 -- Copyright (C) 2010 Florian Zeitz
2 --
3 -- This file is MIT/X11 licensed. Please see the
4 -- COPYING file in the source package for more information.
5 --
6
7 -- <session xmlns="http://prosody.im/streams/c2s" jid="alice@example.com/brussels">
8 -- <encrypted/>
9 -- <compressed/>
10 -- </session>
11
12 -- <session xmlns="http://prosody.im/streams/s2s" jid="example.com">
13 -- <encrypted>
14 -- <valid/> / <invalid/>
15 -- </encrypted>
16 -- <compressed/>
17 -- <in/> / <out/>
18 -- </session>
19
20 local st = require "util.stanza";
21 local uuid_generate = require "util.uuid".generate;
22 local is_admin = require "usermanager".is_admin;
23 local pubsub = require "util.pubsub";
24 local jid_bare = require "util.jid".bare;
25 local lfs = require "lfs";
26 local open = io.open;
27 local stat = lfs.attributes;
28
29 module:set_global();
30
31 local service = {};
32
33 local http_base = module.path:gsub("/[^/]+$","") .. "/www_files/";
34
35 local xmlns_adminsub = "http://prosody.im/adminsub";
36 local xmlns_c2s_session = "http://prosody.im/streams/c2s";
37 local xmlns_s2s_session = "http://prosody.im/streams/s2s";
38
39 local mime_map = {
40 html = "text/html";
41 xml = "text/xml";
42 js = "text/javascript";
43 css = "text/css";
44 };
45
46 local idmap = {};
47
48 function add_client(session, host)
49 local name = session.full_jid;
50 local id = idmap[name];
51 if not id then
52 id = uuid_generate();
53 idmap[name] = id;
54 end
55 local item = st.stanza("item", { id = id }):tag("session", {xmlns = xmlns_c2s_session, jid = name}):up();
56 if session.secure then
57 item:tag("encrypted"):up();
58 end
59 if session.compressed then
60 item:tag("compressed"):up();
61 end
62 service[host]:publish(xmlns_c2s_session, host, id, item);
63 module:log("debug", "Added client " .. name);
64 end
65
66 function del_client(session, host)
67 local name = session.full_jid;
68 local id = idmap[name];
69 if id then
70 local notifier = st.stanza("retract", { id = id });
71 service[host]:retract(xmlns_c2s_session, host, id, notifier);
72 end
73 end
74
75 function add_host(session, type, host)
76 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host);
77 local id = idmap[name.."_"..type];
78 if not id then
79 id = uuid_generate();
80 idmap[name.."_"..type] = id;
81 end
82 local item = st.stanza("item", { id = id }):tag("session", {xmlns = xmlns_s2s_session, jid = name})
83 :tag(type):up();
84 if session.secure then
85 if session.cert_identity_status == "valid" then
86 item:tag("encrypted"):tag("valid"):up():up();
87 else
88 item:tag("encrypted"):tag("invalid"):up():up();
89 end
90 end
91 if session.compressed then
92 item:tag("compressed"):up();
93 end
94 service[host]:publish(xmlns_s2s_session, host, id, item);
95 module:log("debug", "Added host " .. name .. " s2s" .. type);
96 end
97
98 function del_host(session, type, host)
99 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host);
100 local id = idmap[name.."_"..type];
101 if id then
102 local notifier = st.stanza("retract", { id = id });
103 service[host]:retract(xmlns_s2s_session, host, id, notifier);
104 end
105 end
106
107 function serve_file(event, path)
108 local full_path = http_base .. path;
109
110 if stat(full_path, "mode") == "directory" then
111 if stat(full_path.."/index.html", "mode") == "file" then
112 return serve_file(event, path.."/index.html");
113 end
114 return 403;
115 end
116
117 local f, err = open(full_path, "rb");
118 if not f then
119 return 404;
120 end
121
122 local data = f:read("*a");
123 f:close();
124 if not data then
125 return 403;
126 end
127
128 local ext = path:match("%.([^.]*)$");
129 event.response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known
130 return data;
131 end
132
133 function module.add_host(module)
134 module:depends("http");
135 module:provides("http", {
136 name = "admin";
137 route = {
138 ["/"] = function(event)
139 event.response.headers.location = event.request.path .. "/";
140 return 301;
141 end;
142 ["/*"] = serve_file;
143 }
144 });
145 end
146
147 prosody.events.add_handler("server-started", function ()
148 for host_name, host_table in pairs(hosts) do
149 service[host_name] = pubsub.new({
150 broadcaster = function(node, jids, item) return simple_broadcast(node, jids, item, host_name) end;
151 normalize_jid = jid_bare;
152 get_affiliation = function(jid) return get_affiliation(jid, host_name) end;
153 capabilities = {
154 member = {
155 create = false;
156 publish = false;
157 retract = false;
158 get_nodes = true;
159
160 subscribe = true;
161 unsubscribe = true;
162 get_subscription = true;
163 get_subscriptions = true;
164 get_items = true;
165
166 subscribe_other = false;
167 unsubscribe_other = false;
168 get_subscription_other = false;
169 get_subscriptions_other = false;
170
171 be_subscribed = true;
172 be_unsubscribed = true;
173
174 set_affiliation = false;
175 };
176
177 owner = {
178 create = true;
179 publish = true;
180 retract = true;
181 get_nodes = true;
182
183 subscribe = true;
184 unsubscribe = true;
185 get_subscription = true;
186 get_subscriptions = true;
187 get_items = true;
188
189 subscribe_other = true;
190 unsubscribe_other = true;
191 get_subscription_other = true;
192 get_subscriptions_other = true;
193
194 be_subscribed = true;
195 be_unsubscribed = true;
196
197 set_affiliation = true;
198 };
199 };
200 });
201
202 if not select(2, service[host_name]:get_nodes(true))[xmlns_s2s_session] then
203 local ok, errmsg = service[host_name]:create(xmlns_s2s_session, true);
204 if not ok then
205 module:log("warn", "Could not create node " .. xmlns_s2s_session .. ": " .. tostring(errmsg));
206 else
207 service[host_name]:set_affiliation(xmlns_s2s_session, true, host_name, "owner")
208 end
209 end
210
211 for remotehost, session in pairs(host_table.s2sout) do
212 if session.type ~= "s2sout_unauthed" then
213 add_host(session, "out", host_name);
214 end
215 end
216 for session in pairs(incoming_s2s) do
217 if session.to_host == host_name then
218 add_host(session, "in", host_name);
219 end
220 end
221
222 if not select(2, service[host_name]:get_nodes(true))[xmlns_c2s_session] then
223 local ok, errmsg = service[host_name]:create(xmlns_c2s_session, true);
224 if not ok then
225 module:log("warn", "Could not create node " .. xmlns_c2s_session .. ": " .. tostring(errmsg));
226 else
227 service[host_name]:set_affiliation(xmlns_c2s_session, true, host_name, "owner")
228 end
229 end
230
231 for username, user in pairs(host_table.sessions or {}) do
232 for resource, session in pairs(user.sessions or {}) do
233 add_client(session, host_name);
234 end
235 end
236
237 host_table.events.add_handler("iq/host/http://prosody.im/adminsub:adminsub", function(event)
238 local origin, stanza = event.origin, event.stanza;
239 local adminsub = stanza.tags[1];
240 local action = adminsub.tags[1];
241 local reply;
242 if action.name == "subscribe" then
243 local ok, ret = service[host_name]:add_subscription(action.attr.node, stanza.attr.from, stanza.attr.from);
244 if ok then
245 reply = st.reply(stanza)
246 :tag("adminsub", { xmlns = xmlns_adminsub });
247 else
248 reply = st.error_reply(stanza, "cancel", ret);
249 end
250 elseif action.name == "unsubscribe" then
251 local ok, ret = service[host_name]:remove_subscription(action.attr.node, stanza.attr.from, stanza.attr.from);
252 if ok then
253 reply = st.reply(stanza)
254 :tag("adminsub", { xmlns = xmlns_adminsub });
255 else
256 reply = st.error_reply(stanza, "cancel", ret);
257 end
258 elseif action.name == "items" then
259 local node = action.attr.node;
260 local ok, ret = service[host_name]:get_items(node, stanza.attr.from);
261 if not ok then
262 return origin.send(st.error_reply(stanza, "cancel", ret));
263 end
264
265 local data = st.stanza("items", { node = node });
266 for _, entry in pairs(ret) do
267 data:add_child(entry);
268 end
269 if data then
270 reply = st.reply(stanza)
271 :tag("adminsub", { xmlns = xmlns_adminsub })
272 :add_child(data);
273 else
274 reply = st.error_reply(stanza, "cancel", "item-not-found");
275 end
276 elseif action.name == "adminfor" then
277 local data = st.stanza("adminfor");
278 for host_name in pairs(hosts) do
279 if is_admin(stanza.attr.from, host_name) then
280 data:tag("item"):text(host_name):up();
281 end
282 end
283 reply = st.reply(stanza)
284 :tag("adminsub", { xmlns = xmlns_adminsub })
285 :add_child(data);
286 else
287 reply = st.error_reply(stanza, "feature-not-implemented");
288 end
289 return origin.send(reply);
290 end);
291
292 host_table.events.add_handler("resource-bind", function(event)
293 add_client(event.session, host_name);
294 end);
295
296 host_table.events.add_handler("resource-unbind", function(event)
297 del_client(event.session, host_name);
298 service[host_name]:remove_subscription(xmlns_c2s_session, host_name, event.session.full_jid);
299 service[host_name]:remove_subscription(xmlns_s2s_session, host_name, event.session.full_jid);
300 end);
301
302 host_table.events.add_handler("s2sout-established", function(event)
303 add_host(event.session, "out", host_name);
304 end);
305
306 host_table.events.add_handler("s2sin-established", function(event)
307 add_host(event.session, "in", host_name);
308 end);
309
310 host_table.events.add_handler("s2sout-destroyed", function(event)
311 del_host(event.session, "out", host_name);
312 end);
313
314 host_table.events.add_handler("s2sin-destroyed", function(event)
315 del_host(event.session, "in", host_name);
316 end);
317
318 end
319 end);
320
321 function simple_broadcast(node, jids, item, host)
322 item = st.clone(item);
323 item.attr.xmlns = nil; -- Clear the pubsub namespace
324 local message = st.message({ from = host, type = "headline" })
325 :tag("event", { xmlns = xmlns_adminsub .. "#event" })
326 :tag("items", { node = node })
327 :add_child(item);
328 for jid in pairs(jids) do
329 module:log("debug", "Sending notification to %s", jid);
330 message.attr.to = jid;
331 core_post_stanza(hosts[host], message);
332 end
333 end
334
335 function get_affiliation(jid, host)
336 local bare_jid = jid_bare(jid);
337 if is_admin(bare_jid, host) then
338 return "member";
339 else
340 return "none";
341 end
342 end