Software /
code /
prosody-modules
File
mod_admin_web/admin_web/mod_admin_web.lua @ 737:e4ea03b060ed
mod_archive: switch from/to
The XEP-0136 is not very explicit about the meening of <from> and <to>
elements, but the examples are clear: <from> means it comes from the user in
the 'with' attribute of the collection.
That is the opposite of what is currently implemented in that module.
So for better compatibility with complient clients, this switch the 'from' and
'to' fields
author | Olivier Goffart <ogoffart@woboq.com> |
---|---|
date | Wed, 04 Jul 2012 14:08:43 +0200 |
parent | 696:da69b65288e4 |
child | 753:9d5731af2c27 |
line wrap: on
line source
-- Copyright (C) 2010 Florian Zeitz -- -- This file is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- -- <session xmlns="http://prosody.im/streams/c2s" jid="alice@example.com/brussels"> -- <encrypted/> -- <compressed/> -- </session> -- <session xmlns="http://prosody.im/streams/s2s" jid="example.com"> -- <encrypted> -- <valid/> / <invalid/> -- </encrypted> -- <compressed/> -- <in/> / <out/> -- </session> local st = require "util.stanza"; 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; local stat = lfs.attributes; module:set_global(); local service = {}; 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"; js = "text/javascript"; css = "text/css"; }; local idmap = {}; function add_client(session, host) local name = session.full_jid; local id = idmap[name]; if not id then id = uuid_generate(); idmap[name] = id; end local item = st.stanza("item", { id = id }):tag("session", {xmlns = xmlns_c2s_session, jid = name}):up(); if session.secure then item:tag("encrypted"):up(); end if session.compressed then item:tag("compressed"):up(); end service[host]:publish(xmlns_c2s_session, host, id, item); module:log("debug", "Added client " .. name); end function del_client(session, host) local name = session.full_jid; local id = idmap[name]; if id then local notifier = st.stanza("retract", { id = id }); service[host]:retract(xmlns_c2s_session, host, id, notifier); end end function add_host(session, type, host) local name = (type == "out" and session.to_host) or (type == "in" and session.from_host); local id = idmap[name.."_"..type]; if not id then id = uuid_generate(); idmap[name.."_"..type] = id; end local item = st.stanza("item", { id = id }):tag("session", {xmlns = xmlns_s2s_session, jid = name}) :tag(type):up(); if session.secure then if session.cert_identity_status == "valid" then item:tag("encrypted"):tag("valid"):up():up(); else item:tag("encrypted"):tag("invalid"):up():up(); end end if session.compressed then item:tag("compressed"):up(); end service[host]:publish(xmlns_s2s_session, host, id, item); module:log("debug", "Added host " .. name .. " s2s" .. type); end function del_host(session, type, host) local name = (type == "out" and session.to_host) or (type == "in" and session.from_host); local id = idmap[name.."_"..type]; if id then local notifier = st.stanza("retract", { id = id }); service[host]:retract(xmlns_s2s_session, host, id, notifier); 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; end if level < 0 then return nil; end 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; 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; 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" }); 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; 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; set_affiliation = false; }; owner = { create = true; publish = true; retract = true; get_nodes = true; subscribe = true; unsubscribe = true; get_subscription = true; get_subscriptions = true; get_items = true; subscribe_other = true; unsubscribe_other = true; get_subscription_other = true; get_subscriptions_other = true; be_subscribed = true; be_unsubscribed = true; set_affiliation = true; }; }; }); 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)); 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); end end for session in pairs(incoming_s2s) do if session.to_host == host_name then add_host(session, "in", host_name); 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); 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") 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 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); host_table.events.add_handler("s2sout-established", function(event) add_host(event.session, "out", host_name); end); host_table.events.add_handler("s2sin-established", function(event) add_host(event.session, "in", host_name); end); host_table.events.add_handler("s2sout-destroyed", function(event) del_host(event.session, "out", host_name); end); host_table.events.add_handler("s2sin-destroyed", function(event) del_host(event.session, "in", host_name); end); end end); function simple_broadcast(node, jids, item, host) item = st.clone(item); item.attr.xmlns = nil; -- Clear the pubsub namespace local message = st.message({ from = host, type = "headline" }) :tag("event", { xmlns = xmlns_adminsub .. "#event" }) :tag("items", { node = node }) :add_child(item); for jid in pairs(jids) do module:log("debug", "Sending notification to %s", jid); message.attr.to = jid; core_post_stanza(hosts[host], message); end end function get_affiliation(jid, host) local bare_jid = jid_bare(jid); if is_admin(bare_jid, host) then return "member"; else return "none"; end end