Software /
code /
prosody-modules
Changeset
288:9233d7ee3c09
mod_admin_web: Initial PoC commit
author | Florian Zeitz <florob@babelmonkeys.de> |
---|---|
date | Fri, 17 Dec 2010 03:34:53 +0100 |
parents | 287:6144fe6161f1 |
children | 289:415034fd38c2 |
files | mod_admin_web/admin_web/get_deps.lua mod_admin_web/admin_web/mod_admin_web.lua mod_admin_web/admin_web/www_files/index.html mod_admin_web/admin_web/www_files/js/main.js mod_admin_web/admin_web/www_files/style.css |
diffstat | 5 files changed, 405 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_web/admin_web/get_deps.lua Fri Dec 17 03:34:53 2010 +0100 @@ -0,0 +1,30 @@ +#!/usr/bin/env lua + +files = { + ["www_files/strophejs.tar.gz"] = "http://download.github.com/metajack-strophejs-release-1.0-0-g1581b37.tar.gz"; + ["www_files/js/jquery-1.4.4.min.js"] = "http://code.jquery.com/jquery-1.4.4.min.js"; +} + +function fetch(url) + local http = require "socket.http"; + local body, status = http.request(url); + if status == 200 then + return body; + end + return false, "HTTP status code: "..tostring(status); +end + +for filename, url in pairs(files) do + file = io.open(filename, "w+"); + data, error = fetch(url); + if data then + file:write(data); + else + print("Error: " .. error); + end + file:close(); +end + +os.execute("cd www_files && tar xzf strophejs.tar.gz"); +os.execute("cd www_files/metajack-strophejs-3ada7f5 && make strophe.js && cp strophe.js ../js/strophe.js"); +os.execute("rm -r www_files/strophejs.tar.gz www_files/metajack-strophejs-3ada7f5");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_web/admin_web/mod_admin_web.lua Fri Dec 17 03:34:53 2010 +0100 @@ -0,0 +1,150 @@ +-- 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/s2s" jid="example.com"> +-- <encrypted/> +-- <compressed/> +-- <in/> / <out/> +-- </session> + +local stanza = require "util.stanza"; +local uuid_generate = require "util.uuid".generate; +local httpserver = require "net.httpserver"; +local lfs = require "lfs"; +local open = io.open; +local stat = lfs.attributes; + +local host = module:get_host(); +local service = config.get("*", "core", "webadmin_pubsub_host") or ("pubsub." .. host); + +local http_base = (prosody.paths.plugins or "./plugins/") .. "admin_web/www_files"; + +local xmlns_sessions = "http://prosody.im/streams/s2s"; + +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_host(session, type) + 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 = stanza.stanza("item", { id = id }):tag("session", {xmlns = xmlns_sessions, jid = name}) + :tag(type):up(); + if session.secure then + item:tag("encrypted"):up(); + end + if session.compressed then + item:tag("compressed"):up(); + end + hosts[service].modules.pubsub.service:publish(xmlns_sessions, host, id, item); + module:log("debug", "Added host " .. name .. " s2s" .. type); +end + +function del_host(session, type) + 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 = stanza.stanza("retract", { id = id }); + hosts[service].modules.pubsub.service:retract(xmlns_sessions, 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) + 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(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 = path:gsub("^/[^/]+", ""); -- Strip /admin/ + return serve_file(path); +end + +function module.load() + local host_session = prosody.hosts[host]; + local http_conf = config.get("*", "core", "webadmin_http_ports"); + + hosts[service].modules.pubsub.service:create(xmlns_sessions, host); + + for remotehost, session in pairs(host_session.s2sout) do + if session.type ~= "s2sout_unauthed" then + add_host(session, "out"); + end + end + for session in pairs(incoming_s2s) do + if session.to_host == host then + add_host(session, "in"); + end + end + + httpserver.new_from_config(http_conf, handle_file_request, { base = "admin" }); +end + +module:hook("s2sout-established", function(event) + add_host(event.session, "out"); +end); + +module:hook("s2sin-established", function(event) + add_host(event.session, "in"); +end); + +module:hook("s2sout-destroyed", function(event) + del_host(event.session, "out"); +end); + +module:hook("s2sin-destroyed", function(event) + del_host(event.session, "in"); +end);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_web/admin_web/www_files/index.html Fri Dec 17 03:34:53 2010 +0100 @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" + "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>S2S</title> +<link rel="stylesheet" type="text/css" href="style.css" /> +<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" /> +<script type="text/javascript" src="js/jquery-1.4.4.min.js"></script> +<script type="text/javascript" src="js/strophe.js"></script> +<script type="text/javascript" src="js/main.js"></script> +</head> +<body> +<div id='login'> + <form id='cred' name='cred'> + <label for='jid'>JID:</label><br/> + <input type='text' id='jid' /> + <br /> + <label for='pass'>Password:</label><br/> + <input type='password' id='pass' /> + <br /> + <input type='submit' id='connect' value='connect' /> + </form> +</div> + +<div id='main'> + <div class="container"> + Incomming S2S connections: + <ul id="s2sin"></ul> + </div> + <div class="container"> + Outgoing S2S connections: + <ul id="s2sout"></ul> + </div> +</div> + +<div id='log_container'> + <a id='log_toggle' href='#'>Status Log :</a> + <div id='log'></div> +</div> +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_web/admin_web/www_files/js/main.js Fri Dec 17 03:34:53 2010 +0100 @@ -0,0 +1,136 @@ +var BOSH_SERVICE = 'http://localhost:5280/http-bind/'; +var show_log = false; + +Strophe.addNamespace('S2SPUBSUB', 'http://prosody.im/streams/s2s'); +Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub'); +Strophe.addNamespace('CAPS', 'http://jabber.org/protocol/caps'); + +var localJID = null; +var connection = null; + +var pubsubHost = null; /* TODO: Replace this inside Lua */ + +function log(msg) { + var entry = $('<div></div>').append(document.createTextNode(msg)); + $('#log').append(entry); +} + +function rawInput(data) { + log('RECV: ' + data); +} + +function rawOutput(data) { + log('SENT: ' + data); +} + +function _cbNewS2S(e) { + var items, retract, id, jid; + items = e.getElementsByTagName('item'); + for (i = 0; i < items.length; i++) { + id = items[i].attributes['id'].value; + jid = items[i].getElementsByTagName('session')[0].attributes['jid'].value; + if (items[i].getElementsByTagName('out')[0]) { + $('#s2sout').append('<li id="' + id + '">' + jid + '</li>'); + } else { + $('#s2sin').append('<li id="' + id + '">' + jid + '</li>'); + } + } + retract = e.getElementsByTagName('retract')[0]; + if (retract) { + id = retract.attributes['id'].value; + $('#' + id).remove(); + } + return true; +} + +function onConnect(status) { + if (status == Strophe.Status.CONNECTING) { + log('Strophe is connecting.'); + } else if (status == Strophe.Status.CONNFAIL) { + log('Strophe failed to connect.'); + showConnect(); + } else if (status == Strophe.Status.DISCONNECTING) { + log('Strophe is disconnecting.'); + } else if (status == Strophe.Status.DISCONNECTED) { + log('Strophe is disconnected.'); + showConnect(); + } else if (status == Strophe.Status.AUTHFAIL) { + log('Authentication failed'); + if (connection) { + connection.disconnect(); + } + } else if (status == Strophe.Status.CONNECTED) { + log('Strophe is connected.'); + showDisconnect(); + pubsubHost = 'pubsub.' + connection.domain; + connection.send($iq({to: pubsubHost, type: 'set', id: connection.getUniqueId()}).c('pubsub', {xmlns: Strophe.NS.PUBSUB}) + .c('subscribe', {node: Strophe.NS.S2SPUBSUB, jid: connection.jid})); + connection.addHandler(_cbNewS2S, Strophe.NS.PUBSUB + '#event', 'message'); + connection.sendIQ($iq({to: pubsubHost, type: 'get', id: connection.getUniqueId()}).c('pubsub', {xmlns: Strophe.NS.PUBSUB}) + .c('items', {node: Strophe.NS.S2SPUBSUB}), _cbNewS2S); + } +} + +function showConnect() { + var jid = $('#jid'); + var pass = $('#pass'); + var button = $('#connect').get(0); + + button.value = 'connect'; + pass.show(); + jid.show(); + $('.container').hide(); + $('#cred label').show(); + $('#cred br').show(); + $('ul').empty(); +} + +function showDisconnect() { + var jid = $('#jid'); + var pass = $('#pass'); + var button = $('#connect').get(0); + + button.value = 'disconnect'; + pass.hide(); + jid.hide(); + $('.container').show(); + $('#cred label').hide(); + $('#cred br').hide(); +} + +$(document).ready(function () { + connection = new Strophe.Connection(BOSH_SERVICE); + if (show_log) { + $('#log_container').show(); + connection.rawInput = rawInput; + connection.rawOutput = rawOutput; + } + + $("#log_toggle").click(function () { + $("#log").toggle(); + }); + + $('#cred').bind('submit', function (event) { + var button = $('#connect').get(0); + var jid = $('#jid'); + var pass = $('#pass'); + localJID = jid.get(0).value; + + if (button.value == 'connect') { + $('#log').empty(); + connection.connect(localJID, + pass.get(0).value, + onConnect); + } else { + connection.disconnect(); + } + event.preventDefault(); + }); + +}); + +window.onunload = window.onbeforeunload = function() { + if (connection) { + connection.disconnect(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_web/admin_web/www_files/style.css Fri Dec 17 03:34:53 2010 +0100 @@ -0,0 +1,47 @@ +label { + margin-right: 0.5em +} + +input, textarea, select { + margin: 0.25em; + margin-left: 1em +} + +body { + padding: 1em; + background: #f29b00; + color: #000000 +} + +a { + color: #0000FF +} + +#log_container { + clear: both; + display: none +} + +.container { + margin: 2em; + padding: 0.5em; + border: solid; + float: left; + display: none +} + +#login{ + float: left; + margin-right: 2em; + padding: 1em; + background: #6197DF; + color: #000000 +} + +#cred input[type="submit"] { + margin-left: 0em +} + +#main { + float: left +}