Software / code / prosody-modules
Comparison
mod_admin_web/admin_web/mod_admin_web.lua @ 288:9233d7ee3c09
mod_admin_web: Initial PoC commit
| author | Florian Zeitz <florob@babelmonkeys.de> |
|---|---|
| date | Fri, 17 Dec 2010 03:34:53 +0100 |
| child | 292:a9e69088e678 |
comparison
equal
deleted
inserted
replaced
| 287:6144fe6161f1 | 288:9233d7ee3c09 |
|---|---|
| 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/s2s" jid="example.com"> | |
| 8 -- <encrypted/> | |
| 9 -- <compressed/> | |
| 10 -- <in/> / <out/> | |
| 11 -- </session> | |
| 12 | |
| 13 local stanza = require "util.stanza"; | |
| 14 local uuid_generate = require "util.uuid".generate; | |
| 15 local httpserver = require "net.httpserver"; | |
| 16 local lfs = require "lfs"; | |
| 17 local open = io.open; | |
| 18 local stat = lfs.attributes; | |
| 19 | |
| 20 local host = module:get_host(); | |
| 21 local service = config.get("*", "core", "webadmin_pubsub_host") or ("pubsub." .. host); | |
| 22 | |
| 23 local http_base = (prosody.paths.plugins or "./plugins/") .. "admin_web/www_files"; | |
| 24 | |
| 25 local xmlns_sessions = "http://prosody.im/streams/s2s"; | |
| 26 | |
| 27 local response_400 = { status = "400 Bad Request", body = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(" }; | |
| 28 local response_403 = { status = "403 Forbidden", body = "<h1>Forbidden</h1>You don't have permission to view the contents of this directory :(" }; | |
| 29 local response_404 = { status = "404 Not Found", body = "<h1>Page Not Found</h1>Sorry, we couldn't find what you were looking for :(" }; | |
| 30 | |
| 31 local mime_map = { | |
| 32 html = "text/html"; | |
| 33 xml = "text/xml"; | |
| 34 js = "text/javascript"; | |
| 35 css = "text/css"; | |
| 36 }; | |
| 37 | |
| 38 local idmap = {}; | |
| 39 | |
| 40 function add_host(session, type) | |
| 41 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host); | |
| 42 local id = idmap[name.."_"..type]; | |
| 43 if not id then | |
| 44 id = uuid_generate(); | |
| 45 idmap[name.."_"..type] = id; | |
| 46 end | |
| 47 local item = stanza.stanza("item", { id = id }):tag("session", {xmlns = xmlns_sessions, jid = name}) | |
| 48 :tag(type):up(); | |
| 49 if session.secure then | |
| 50 item:tag("encrypted"):up(); | |
| 51 end | |
| 52 if session.compressed then | |
| 53 item:tag("compressed"):up(); | |
| 54 end | |
| 55 hosts[service].modules.pubsub.service:publish(xmlns_sessions, host, id, item); | |
| 56 module:log("debug", "Added host " .. name .. " s2s" .. type); | |
| 57 end | |
| 58 | |
| 59 function del_host(session, type) | |
| 60 local name = (type == "out" and session.to_host) or (type == "in" and session.from_host); | |
| 61 local id = idmap[name.."_"..type]; | |
| 62 if id then | |
| 63 local notifier = stanza.stanza("retract", { id = id }); | |
| 64 hosts[service].modules.pubsub.service:retract(xmlns_sessions, host, id, notifier); | |
| 65 end | |
| 66 end | |
| 67 | |
| 68 local function preprocess_path(path) | |
| 69 if path:sub(1,1) ~= "/" then | |
| 70 path = "/"..path; | |
| 71 end | |
| 72 local level = 0; | |
| 73 for component in path:gmatch("([^/]+)/") do | |
| 74 if component == ".." then | |
| 75 level = level - 1; | |
| 76 elseif component ~= "." then | |
| 77 level = level + 1; | |
| 78 end | |
| 79 if level < 0 then | |
| 80 return nil; | |
| 81 end | |
| 82 end | |
| 83 return path; | |
| 84 end | |
| 85 | |
| 86 function serve_file(path) | |
| 87 local full_path = http_base..path; | |
| 88 if stat(full_path, "mode") == "directory" then | |
| 89 if stat(full_path.."/index.html", "mode") == "file" then | |
| 90 return serve_file(path.."/index.html"); | |
| 91 end | |
| 92 return response_403; | |
| 93 end | |
| 94 local f, err = open(full_path, "rb"); | |
| 95 if not f then return response_404; end | |
| 96 local data = f:read("*a"); | |
| 97 f:close(); | |
| 98 if not data then | |
| 99 return response_403; | |
| 100 end | |
| 101 local ext = path:match("%.([^.]*)$"); | |
| 102 local mime = mime_map[ext]; -- Content-Type should be nil when not known | |
| 103 return { | |
| 104 headers = { ["Content-Type"] = mime; }; | |
| 105 body = data; | |
| 106 }; | |
| 107 end | |
| 108 | |
| 109 local function handle_file_request(method, body, request) | |
| 110 local path = preprocess_path(request.url.path); | |
| 111 if not path then return response_400; end | |
| 112 path = path:gsub("^/[^/]+", ""); -- Strip /admin/ | |
| 113 return serve_file(path); | |
| 114 end | |
| 115 | |
| 116 function module.load() | |
| 117 local host_session = prosody.hosts[host]; | |
| 118 local http_conf = config.get("*", "core", "webadmin_http_ports"); | |
| 119 | |
| 120 hosts[service].modules.pubsub.service:create(xmlns_sessions, host); | |
| 121 | |
| 122 for remotehost, session in pairs(host_session.s2sout) do | |
| 123 if session.type ~= "s2sout_unauthed" then | |
| 124 add_host(session, "out"); | |
| 125 end | |
| 126 end | |
| 127 for session in pairs(incoming_s2s) do | |
| 128 if session.to_host == host then | |
| 129 add_host(session, "in"); | |
| 130 end | |
| 131 end | |
| 132 | |
| 133 httpserver.new_from_config(http_conf, handle_file_request, { base = "admin" }); | |
| 134 end | |
| 135 | |
| 136 module:hook("s2sout-established", function(event) | |
| 137 add_host(event.session, "out"); | |
| 138 end); | |
| 139 | |
| 140 module:hook("s2sin-established", function(event) | |
| 141 add_host(event.session, "in"); | |
| 142 end); | |
| 143 | |
| 144 module:hook("s2sout-destroyed", function(event) | |
| 145 del_host(event.session, "out"); | |
| 146 end); | |
| 147 | |
| 148 module:hook("s2sin-destroyed", function(event) | |
| 149 del_host(event.session, "in"); | |
| 150 end); |