Diff

mod_admin_web/admin_web/mod_admin_web.lua @ 753:9d5731af2c27

Merge with Oliver Gerlich
author Matthew Wild <mwild1@gmail.com>
date Fri, 27 Jul 2012 14:29:59 +0100
parent 696:da69b65288e4
parent 747:e54b92a26d40
child 761:48f8b312a509
line wrap: on
line diff
--- a/mod_admin_web/admin_web/mod_admin_web.lua	Mon Jun 11 22:32:45 2012 +0200
+++ b/mod_admin_web/admin_web/mod_admin_web.lua	Fri Jul 27 14:29:59 2012 +0100
@@ -21,7 +21,6 @@
 local uuid_generate = require "util.uuid".generate;
 local is_admin = require "core.usermanager".is_admin;
 local pubsub = require "util.pubsub";
-local httpserver = require "net.httpserver";
 local jid_bare = require "util.jid".bare;
 local lfs = require "lfs";
 local open = io.open;
@@ -31,17 +30,12 @@
 
 local service = {};
 
-local http_base = module.path:gsub("/[^/]+$","") .. "/www_files";
+local http_base = module.path:gsub("/[^/]+$","") .. "/www_files/";
 
 local xmlns_adminsub = "http://prosody.im/adminsub";
 local xmlns_c2s_session = "http://prosody.im/streams/c2s";
 local xmlns_s2s_session = "http://prosody.im/streams/s2s";
 
-local response_301 = { status = "301 Moved Permanently" };
-local response_400 = { status = "400 Bad Request", body = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(" };
-local response_403 = { status = "403 Forbidden", body = "<h1>Forbidden</h1>You don't have permission to view the contents of this directory :(" };
-local response_404 = { status = "404 Not Found", body = "<h1>Page Not Found</h1>Sorry, we couldn't find what you were looking for :(" };
-
 local mime_map = {
 	html = "text/html";
 	xml = "text/xml";
@@ -110,238 +104,222 @@
 	end
 end
 
-local function preprocess_path(path)
-	if path:sub(1,1) ~= "/" then
-		path = "/"..path;
-	end
-	local level = 0;
-	for component in path:gmatch("([^/]+)/") do
-		if component == ".." then
-			level = level - 1;
-		elseif component ~= "." then
-			level = level + 1;
+function serve_file(event, path)
+	local full_path = http_base .. path;
+
+	if stat(full_path, "mode") == "directory" then
+		if stat(full_path.."/index.html", "mode") == "file" then
+			return serve_file(event, path.."/index.html");
 		end
-		if level < 0 then
-			return nil;
-		end
+		return 403;
 	end
-	return path;
-end
 
-function serve_file(path, base)
-	local full_path = http_base..path;
-	if stat(full_path, "mode") == "directory" then
-		if not path:find("/$") then
-			local response = response_301;
-			response.headers = { ["Location"] = base .. "/" };
-			return response;
-		end
-		if stat(full_path.."/index.html", "mode") == "file" then
-			return serve_file(path.."/index.html");
-		end
-		return response_403;
+	local f, err = open(full_path, "rb");
+	if not f then
+		return 404;
 	end
-	local f, err = open(full_path, "rb");
-	if not f then return response_404; end
+
 	local data = f:read("*a");
 	f:close();
 	if not data then
-		return response_403;
+		return 403;
 	end
-	local ext = path:match("%.([^.]*)$");
-	local mime = mime_map[ext]; -- Content-Type should be nil when not known
-	return {
-		headers = { ["Content-Type"] = mime; };
-		body = data;
-	};
-end
 
-local function handle_file_request(method, body, request)
-	local path = preprocess_path(request.url.path);
-	if not path then return response_400; end
-	path_stripped = path:gsub("^/[^/]+", ""); -- Strip /admin/
-	return serve_file(path_stripped, path);
-end
-
-function module.load()
-	local http_conf = config.get("*", "core", "webadmin_http_ports");
-
-	httpserver.new_from_config(http_conf, handle_file_request, { base = "admin" });
+	local ext = path:match("%.([^.]*)$");
+	event.response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known
+	return data;
 end
 
-prosody.events.add_handler("server-started", function ()
-	for host_name, host_table in pairs(hosts) do
-		service[host_name] = pubsub.new({
-			broadcaster = function(node, jids, item) return simple_broadcast(node, jids, item, host_name) end;
-			normalize_jid = jid_bare;
-			get_affiliation = function(jid) return get_affiliation(jid, host_name) end;
-			capabilities = {
-				member = {
-					create = false;
-					publish = false;
-					retract = false;
-					get_nodes = true;
+function module.add_host(module)
+	-- Setup HTTP server
+	module:depends("http");
+	module:provides("http", {
+		name = "admin";
+		route = {
+			["GET"] = function(event)
+				event.response.headers.location = event.request.path .. "/";
+				return 301;
+			end;
+			["GET /*"] = serve_file;
+		}
+	});
+
+	-- Setup adminsub service
+	local ok, err;
+	service[module.host] = pubsub.new({
+		broadcaster = function(node, jids, item) return simple_broadcast(node, jids, item, module.host) end;
+		normalize_jid = jid_bare;
+		get_affiliation = function(jid) return get_affiliation(jid, module.host) end;
+		capabilities = {
+			member = {
+				create = false;
+				publish = false;
+				retract = false;
+				get_nodes = true;
 
-					subscribe = true;
-					unsubscribe = true;
-					get_subscription = true;
-					get_subscriptions = true;
-					get_items = true;
+				subscribe = true;
+				unsubscribe = true;
+				get_subscription = true;
+				get_subscriptions = true;
+				get_items = true;
+
+				subscribe_other = false;
+				unsubscribe_other = false;
+				get_subscription_other = false;
+				get_subscriptions_other = false;
+
+				be_subscribed = true;
+				be_unsubscribed = true;
 
-					subscribe_other = false;
-					unsubscribe_other = false;
-					get_subscription_other = false;
-					get_subscriptions_other = false;
+				set_affiliation = false;
+			};
 
-					be_subscribed = true;
-					be_unsubscribed = true;
+			owner = {
+				create = true;
+				publish = true;
+				retract = true;
+				get_nodes = true;
 
-					set_affiliation = false;
-				};
+				subscribe = true;
+				unsubscribe = true;
+				get_subscription = true;
+				get_subscriptions = true;
+				get_items = true;
 
-				owner = {
-					create = true;
-					publish = true;
-					retract = true;
-					get_nodes = true;
+				subscribe_other = true;
+				unsubscribe_other = true;
+				get_subscription_other = true;
+				get_subscriptions_other = true;
+
+				be_subscribed = true;
+				be_unsubscribed = true;
+
+				set_affiliation = true;
+			};
+		};
+	});
 
-					subscribe = true;
-					unsubscribe = true;
-					get_subscription = true;
-					get_subscriptions = true;
-					get_items = true;
+	-- Create node for s2s sessions
+	ok, err = service[module.host]:create(xmlns_s2s_session, true);
+	if not ok then
+		module:log("warn", "Could not create node " .. xmlns_s2s_session .. ": " .. tostring(err));
+	else
+		service[module.host]:set_affiliation(xmlns_s2s_session, true, module.host, "owner")
+	end
 
-					subscribe_other = true;
-					unsubscribe_other = true;
-					get_subscription_other = true;
-					get_subscriptions_other = true;
+	-- Add outgoing s2s sessions 
+	for remotehost, session in pairs(hosts[module.host].s2sout) do
+		if session.type ~= "s2sout_unauthed" then
+			add_host(session, "out", module.host);
+		end
+	end
 
-					be_subscribed = true;
-					be_unsubscribed = true;
+	-- Add incomming s2s sessions 
+	for session in pairs(incoming_s2s) do
+		if session.to_host == module.host then
+			add_host(session, "in", module.host);
+		end
+	end
 
-					set_affiliation = true;
-				};
-			};
-		});
+	-- Create node for c2s sessions
+	ok, err = service[module.host]:create(xmlns_c2s_session, true);
+	if not ok then
+		module:log("warn", "Could not create node " .. xmlns_c2s_session .. ": " .. tostring(err));
+	else
+		service[module.host]:set_affiliation(xmlns_c2s_session, true, module.host, "owner")
+	end
 
-		if not select(2, service[host_name]:get_nodes(true))[xmlns_s2s_session] then
-			local ok, errmsg = service[host_name]:create(xmlns_s2s_session, true);
-			if not ok then
-				module:log("warn", "Could not create node " .. xmlns_s2s_session .. ": " .. tostring(errmsg));
+	-- Add c2s sessions
+	for username, user in pairs(hosts[module.host].sessions or {}) do
+		for resource, session in pairs(user.sessions or {}) do
+			add_client(session, module.host);
+		end
+	end
+
+	-- Register adminsub handler
+	module:hook("iq/host/http://prosody.im/adminsub:adminsub", function(event)
+		local origin, stanza = event.origin, event.stanza;
+		local adminsub = stanza.tags[1];
+		local action = adminsub.tags[1];
+		local reply;
+		if action.name == "subscribe" then
+			local ok, ret = service[module.host]:add_subscription(action.attr.node, stanza.attr.from, stanza.attr.from);
+			if ok then
+				reply = st.reply(stanza)
+					:tag("adminsub", { xmlns = xmlns_adminsub });
 			else
-				service[host_name]:set_affiliation(xmlns_s2s_session, true, host_name, "owner")
-			end
-		end
-
-		for remotehost, session in pairs(host_table.s2sout) do
-			if session.type ~= "s2sout_unauthed" then
-				add_host(session, "out", host_name);
+				reply = st.error_reply(stanza, "cancel", ret);
 			end
-		end
-		for session in pairs(incoming_s2s) do
-			if session.to_host == host_name then
-				add_host(session, "in", host_name);
+		elseif action.name == "unsubscribe" then
+			local ok, ret = service[module.host]:remove_subscription(action.attr.node, stanza.attr.from, stanza.attr.from);
+			if ok then
+				reply = st.reply(stanza)
+					:tag("adminsub", { xmlns = xmlns_adminsub });
+			else
+				reply = st.error_reply(stanza, "cancel", ret);
 			end
-		end
-
-		if not select(2, service[host_name]:get_nodes(true))[xmlns_c2s_session] then
-			local ok, errmsg = service[host_name]:create(xmlns_c2s_session, true);
+		elseif action.name == "items" then
+			local node = action.attr.node;
+			local ok, ret = service[module.host]:get_items(node, stanza.attr.from);
 			if not ok then
-				module:log("warn", "Could not create node " .. xmlns_c2s_session .. ": " .. tostring(errmsg));
-			else
-				service[host_name]:set_affiliation(xmlns_c2s_session, true, host_name, "owner")
+				return origin.send(st.error_reply(stanza, "cancel", ret));
 			end
-		end
-
-		for username, user in pairs(host_table.sessions or {}) do
-			for resource, session in pairs(user.sessions or {}) do
-				add_client(session, host_name);
-			end
-		end
 
-		host_table.events.add_handler("iq/host/http://prosody.im/adminsub:adminsub", function(event)
-			local origin, stanza = event.origin, event.stanza;
-			local adminsub = stanza.tags[1];
-			local action = adminsub.tags[1];
-			local reply;
-			if action.name == "subscribe" then
-				local ok, ret = service[host_name]:add_subscription(action.attr.node, stanza.attr.from, stanza.attr.from);
-				if ok then
-					reply = st.reply(stanza)
-						:tag("adminsub", { xmlns = xmlns_adminsub });
-				else
-					reply = st.error_reply(stanza, "cancel", ret);
-				end
-			elseif action.name == "unsubscribe" then
-				local ok, ret = service[host_name]:remove_subscription(action.attr.node, stanza.attr.from, stanza.attr.from);
-				if ok then
-					reply = st.reply(stanza)
-						:tag("adminsub", { xmlns = xmlns_adminsub });
-				else
-					reply = st.error_reply(stanza, "cancel", ret);
-				end
-			elseif action.name == "items" then
-				local node = action.attr.node;
-				local ok, ret = service[host_name]:get_items(node, stanza.attr.from);
-				if not ok then
-					return origin.send(st.error_reply(stanza, "cancel", ret));
-				end
-
-				local data = st.stanza("items", { node = node });
-				for _, entry in pairs(ret) do
-					data:add_child(entry);
-				end
-				if data then
-					reply = st.reply(stanza)
-						:tag("adminsub", { xmlns = xmlns_adminsub })
-							:add_child(data);
-				else
-					reply = st.error_reply(stanza, "cancel", "item-not-found");
-				end
-			elseif action.name == "adminfor" then
-				local data = st.stanza("adminfor");
-				for host_name in pairs(hosts) do
-					if is_admin(stanza.attr.from, host_name) then
-						data:tag("item"):text(host_name):up();
-					end
-				end
+			local data = st.stanza("items", { node = node });
+			for _, entry in pairs(ret) do
+				data:add_child(entry);
+			end
+			if data then
 				reply = st.reply(stanza)
 					:tag("adminsub", { xmlns = xmlns_adminsub })
 						:add_child(data);
 			else
-				reply = st.error_reply(stanza, "feature-not-implemented");
+				reply = st.error_reply(stanza, "cancel", "item-not-found");
+			end
+		elseif action.name == "adminfor" then
+			local data = st.stanza("adminfor");
+			for host_name in pairs(hosts) do
+				if is_admin(stanza.attr.from, host_name) then
+					data:tag("item"):text(host_name):up();
+				end
 			end
-			return origin.send(reply);
-		end);
+			reply = st.reply(stanza)
+				:tag("adminsub", { xmlns = xmlns_adminsub })
+					:add_child(data);
+		else
+			reply = st.error_reply(stanza, "feature-not-implemented");
+		end
+		return origin.send(reply);
+	end);
 
-		host_table.events.add_handler("resource-bind", function(event)
-			add_client(event.session, host_name);
-		end);
-
-		host_table.events.add_handler("resource-unbind", function(event)
-			del_client(event.session, host_name);
-			service[host_name]:remove_subscription(xmlns_c2s_session, host_name, event.session.full_jid);
-			service[host_name]:remove_subscription(xmlns_s2s_session, host_name, event.session.full_jid);
-		end);
+	-- Add/remove c2s sessions
+	module:hook("resource-bind", function(event)
+		add_client(event.session, module.host);
+	end);
 
-		host_table.events.add_handler("s2sout-established", function(event)
-			add_host(event.session, "out", host_name);
-		end);
+	module:hook("resource-unbind", function(event)
+		del_client(event.session, module.host);
+		service[module.host]:remove_subscription(xmlns_c2s_session, module.host, event.session.full_jid);
+		service[module.host]:remove_subscription(xmlns_s2s_session, module.host, event.session.full_jid);
+	end);
 
-		host_table.events.add_handler("s2sin-established", function(event)
-			add_host(event.session, "in", host_name);
-		end);
+	-- Add/remove s2s sessions
+	module:hook("s2sout-established", function(event)
+		add_host(event.session, "out", module.host);
+	end);
 
-		host_table.events.add_handler("s2sout-destroyed", function(event)
-			del_host(event.session, "out", host_name);
-		end);
+	module:hook("s2sin-established", function(event)
+		add_host(event.session, "in", module.host);
+	end);
 
-		host_table.events.add_handler("s2sin-destroyed", function(event)
-			del_host(event.session, "in", host_name);
-		end);
+	module:hook("s2sout-destroyed", function(event)
+		del_host(event.session, "out", module.host);
+	end);
 
-	end
-end);
+	module:hook("s2sin-destroyed", function(event)
+		del_host(event.session, "in", module.host);
+	end);
+end
 
 function simple_broadcast(node, jids, item, host)
 	item = st.clone(item);