Changeset

6202:6d5a19bdd718

mod_openid, mod_json_streams: Remove modules depending on obsolete net.httpserver API These have not been updated for a long time and do not work with Prosody versions from recent years, which is a good indication they are not being used.
author Matthew Wild <mwild1@gmail.com>
date Fri, 14 Feb 2025 14:59:14 +0000
parents 6201:274fb65904a7
children 6203:131b8bfbefb4
files mod_json_streams/README.md mod_json_streams/mod_json_streams.lua mod_json_streams/strophe.jsonstreams.js mod_openid/README.md mod_openid/mod_openid.lua
diffstat 5 files changed, 0 insertions(+), 866 deletions(-) [+]
line wrap: on
line diff
--- a/mod_json_streams/README.md	Thu Feb 13 12:20:46 2025 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
----
-labels:
-- 'Stage-Beta'
-summary: JSON Encodings for XMPP
-...
-
-Introduction
-============
-
-This plugin encodes XMPP as JSON. This is an implementation of
-[XEP-0295: JSON Encodings for
-XMPP](http://xmpp.org/extensions/xep-0295.html).
-
-Simply loading this module makes Prosody accept JSON on C2S streams
-(legacy XML clients are still supported).
-
-For BOSH, it requires mod\_bosh be loaded, and JSON should be directed
-at the `/jsonstreams` HTTP path.
-
-JSON for S2S isn't supported due to the lack of a discovery mechanism,
-so we have left that disabled to stay compatible with legacy XML
-servers.
-
-Configuration
-=============
-
-Just add `"json_streams"` in your config's global `modules_enabled`
-list, for example:
-
-    modules_enabled = {
-        ...
-        "json_streams";
-    }
-
-Strophe.js plugin
-=================
-
-We also developed a [JSON streams
-plugin](http://prosody-modules.googlecode.com/hg/mod_json_streams/strophe.jsonstreams.js)
-for the popular [strophe.js](http://code.stanziq.com/strophe) library.
-
-Just include it like this after including the strophe library, and your
-strophe-based client will be speaking JSON:
-
-    <script type="text/javascript" src="strophe.jsonstreams.js"></script>
-
-Be sure to set the HTTP path to `/jsonstreams`. No other changes are
-required.
-
-Compatibility
-=============
-
-  ------- -------
-  0.8     Works
-  trunk   Works
-  ------- -------
-
-Quirks
-======
-
--   This plugin does not currently work with Prosody's [port
-    multiplexing](http://prosody.im/doc/port_multiplexing) feature
--- a/mod_json_streams/mod_json_streams.lua	Thu Feb 13 12:20:46 2025 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
---
--- XEP-0295: JSON Encodings for XMPP
---
-
-module.host = "*"
-
-local httpserver = require "net.httpserver";
-local filters = require "util.filters"
-local json = require "util.json"
-
-local json_escapes = {
-	["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
-	["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"};
-
-local s_char = string.char;
-for i=0,31 do
-	local ch = s_char(i);
-	if not json_escapes[ch] then json_escapes[ch] = ("\\u%.4X"):format(i); end
-end
-
-local state_out = 0;
-local state_key_before = 1;
-local state_key_in = 2;
-local state_key_escape = 3;
-local state_key_after = 4;
-local state_val_before = 5;
-local state_val_in = 6;
-local state_val_escape = 7;
-local state_val_after = 8;
-
-local whitespace = { [" "] = true, ["\n"] = true, ["\r"] = true, ["\t"] = true };
-function json_decoder()
-	local state = state_out;
-	local quote;
-	local output = "";
-	local buffer = "";
-	return function(input)
-		for ch in input:gmatch(".") do
-			module:log("debug", "%s | %d", ch, state)
-			local final = false;
-			if state == state_out then
-				if whitespace[ch] then
-				elseif ch ~= "{" then return nil, "{ expected";
-				else state = state_key_before end
-			elseif state == state_key_before then
-				if whitespace[ch] then
-				elseif ch ~= "'" and ch ~= "\"" then return nil, "\" expected";
-				else quote = ch; state = state_key_in; end
-			elseif state == state_key_in then
-				if ch == quote then state = state_key_after;
-				elseif ch ~= "s" then return nil, "invalid key, 's' expected"; -- only s as key allowed
-				else end -- ignore key
-			elseif state == state_key_after then
-				if whitespace[ch] then
-				elseif ch ~= ":" then return nil, ": expected";
-				else state = state_val_before; end
-			elseif state == state_val_before then
-				if whitespace[ch] then
-				elseif ch ~= "'" and ch ~= "\"" then return nil, "\" expected";
-				else quote = ch; state = state_val_in; end
-			elseif state == state_val_in then
-				if ch == quote then state = state_val_after;
-				elseif ch == "\\" then state = state_val_escape;
-				else end
-			elseif state == state_val_after then
-				if whitespace[ch] then
-				elseif ch ~= "}" then return nil, "} expected";
-				else state = state_out;
-					final = true;
-				end
-			elseif state == state_val_escape then
-				state = state_val_in;
-			else
-				module:log("error", "Unhandled state: "..state);
-				return nil, "Unhandled state in parser"
-			end
-			buffer = buffer..ch;
-			if final then
-				module:log("debug", "%s", buffer)
-				local tmp;
-				pcall(function() tmp = json.decode(buffer); end);
-				if not tmp then return nil, "Invalid JSON"; end
-				output, buffer = output..tmp.s, "";
-			end
-		end
-		local _ = output; output = "";
-		return _;
-	end;
-end
-
-function filter_hook(session)
-	local determined = false;
-	local is_json = false;
-	local function in_filter(t)
-		if not determined then
-			is_json = (t:sub(1,1) == "{") and json_decoder();
-			determined = true;
-		end
-		if is_json then
-			local s, err = is_json(t);
-			if not err then return s; end
-			session:close("not-well-formed");
-			return;
-		end
-		return t;
-	end
-	local function out_filter(t)
-		if is_json then
-			return '{"s":"' .. t:gsub(".", json_escapes) .. '"}'; -- encode
-		end
-		return t;
-	end
-	filters.add_filter(session, "bytes/in", in_filter,   100);
-	filters.add_filter(session, "bytes/out", out_filter, 100);
-end
-
-function module.load()
-	filters.add_filter_hook(filter_hook);
-end
-function module.unload()
-	filters.remove_filter_hook(filter_hook);
-end
-
-function encode(data)
-	if type(data) == "string" then
-		data = json.encode({ s = data });
-	elseif type(data) == "table" and data.body then
-		data.body = json.encode({ s = data.body });
-		data.headers["Content-Type"] = "application/json";
-	end
-	return data;
-end
-function handle_request(method, body, request)
-	local mod_bosh = modulemanager.get_module("*", "bosh")
-	if mod_bosh then
-		if body and method == "POST" then
-			pcall(function() body = json.decode(body).s; end);
-		end
-		local _send = request.send;
-		function request:send(data) return _send(self, encode(data)); end
-		return encode(mod_bosh.handle_request(method, body, request));
-	end
-	return "<html><body>mod_bosh not loaded</body></html>";
-end
-
-local function setup()
-	local ports = module:get_option("jsonstreams_ports") or { 5280 };
-	httpserver.new_from_config(ports, handle_request, { base = "jsonstreams" });
-end
-if prosody.start_time then -- already started
-	setup();
-else
-	prosody.events.add_handler("server-started", setup);
-end
--- a/mod_json_streams/strophe.jsonstreams.js	Thu Feb 13 12:20:46 2025 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-
-/* jsonstreams plugin
-**
-** This plugin upgrades Strophe to support XEP-0295: JSON Encodings for XMPP
-**
-*/
-
-Strophe.addConnectionPlugin('jsonstreams', {
-    init: function (conn) {
-
-        var parseXMLString = function(xmlStr) {
-			var xmlDoc = null;
-			if (window.ActiveXObject) {
-				xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
-				xmlDoc.async=false;
-				xmlDoc.loadXML(xmlStr);
-			} else {
-				var parser = new DOMParser();
-				xmlDoc = parser.parseFromString(xmlStr, "text/xml");
-			}
-			return xmlDoc;
-        }
-
-        // replace Strophe.Request._newXHR with new jsonstreams version
-        // if JSON is detected
-        if (window.JSON) {
-        	var _newXHR = Strophe.Request.prototype._newXHR;
-            Strophe.Request.prototype._newXHR = function () {
-            	var _xhr = _newXHR.apply(this, arguments);
-                var xhr = {
-                	readyState: 0,
-                	responseText: null,
-                	responseXML: null,
-                	status: null,
-                	open: function(a, b, c) { return _xhr.open(a, b, c) },
-                	abort: function() { _xhr.abort(); },
-                	send: function(data) {
-                		data = JSON.stringify({"s":data});
-                		return _xhr.send(data);
-                	}
-                };
-                var req = this;
-                xhr.onreadystatechange = this.func.bind(null, this);
-                _xhr.onreadystatechange = function() {
-                	xhr.readyState = _xhr.readyState;
-                	if (xhr.readyState != 4) {
-                		xhr.status = 0;
-                		xhr.responseText = "";
-                		xhr.responseXML = null;
-                	} else {
-	                	xhr.status = _xhr.status;
-	               		xhr.responseText = _xhr.responseText;
-	               		xhr.responseXML = _xhr.responseXML;
-	                	if (_xhr.responseText && !(_xhr.responseXML
-	                			&& _xhr.responseXML.documentElement
-	                			&& _xhr.responseXML.documentElement.tagName != "parsererror")) {
-	                		var data = JSON.parse(_xhr.responseText);
-	                		if (data && data.s) {
-	                			xhr.responseText = data.s;
-	                			xhr.responseXML = parseXMLString(data.s);
-	                		}
-	                	}
-	                }
-                	if ("function" == typeof xhr.onreadystatechange) { xhr.onreadystatechange(req); }
-                }
-                return xhr;
-            };
-        } else {
-            Strophe.error("jsonstreams plugin loaded, but JSON not found." +
-                          "  Falling back to native XHR implementation.");
-        }
-    }
-});
--- a/mod_openid/README.md	Thu Feb 13 12:20:46 2025 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
----
-labels:
-- 'Stage-Alpha'
-summary: Enables Prosody to act as an OpenID provider
-...
-
-Introduction
-============
-
-[OpenID](http://openid.net/) is an decentralized authentication
-mechanism for the Web. mod\_openid turns Prosody into an OpenID
-*provider*, allowing users to use their Prosody credentials to
-authenticate with various third party websites.
-
-Caveats
-=======
-
-mod\_openid can best be described as a **proof-of-concept**, it has
-known deficiencies and should **not** be used in the wild as a
-legitimate OpenID provider. mod\_openid was developed using the Prosody
-0.4.x series, it has not been tested with the 0.5.x or later series.
-
-Details
-=======
-
-OpenID works on the basis of a user proving to a third-party they wish
-to authenticate with, an OpenID *relaying party*, that they have claim
-or ownership over a URL, known as an OpenID *identifier*. mod\_openid
-uses Prosody's built in HTTP server to provide every user with an OpenID
-identifier of the form `http://host.domain.tld[:port]/openid/user`,
-which would be the OpenID identifier of the user with a Jabber ID of
-`user@host.domain.tld`.
-
-Usage
-=====
-
-Simply add "mod\_openid" to your modules\_enabled list. You may then use
-the OpenID identifier form as described above as your OpenID identifier.
-The port Prosody's HTTP server will listen on is currently set as 5280,
-meaning the full OpenID identifier of the user `romeo@montague.lit`
-would be `http://montague.lit:5280/openid/romeo`.
-
-Configuration
-=============
-
-mod\_openid has no configuration options as of this time.
-
-TODO
-====
-
-The following is a list of the pending tasks which would have to be done
-to make mod\_openid fully featured. They are generally ranked in order
-of most importance with an estimated degree of difficulty.
-
-1.  Support Prosody 0.6.x series (**Medium**)
-2.  Refactor code (**Medium**)
-    -   The code is pretty messy at the moment, it should be refactored
-        to be more easily understood.
-
-3.  Disable use of "user@domain" OpenID identifier form (*Easy*)
-    -   This is a vestigial feature from the early design, allowing
-        explicit specification of the JID. However the JID can be
-        inferred from the simpler OpenID identifier form.
-
-4.  Use a cryptographically secure Pseudo Random Number Generator (PRNG)
-    (**Medium**)
-    -   This would likely be accomplished using luacrypto which provides
-        a Lua binding to the OpenSSL PRNG.
-
-5.  Make sure OpenID key-value pairs get signed in the right order
-    (***Hard***)
-    -   It is important that the OpenID key-value responses be signed in
-        the proper order so that the signature can be properly verified
-        by the receiving party. This may be complicated by the fact that
-        the iterative ordering of keys in a Lua table is not guaranteed
-        for non-integer keys.
-
-6.  Do an actual match on the OpenID realm (**Medium**)
-    -   The code currently always returns true for matches against an
-        OpenID realm, posing a security risk.
-
-7.  Don't use plain text authentication over HTTP (***Hard***)
-    -   This would require some Javascript to perform a digest.
-
-8.  Return meaningful error responses (**Medium**)
-    -   Most error responses are an HTTP 404 File Not Found, obviously
-        something more meaningful could be returned.
-
-9.  Enable Association (***Hard***)
-    -   Association is a feature of the OpenID specification which
-        reduces the number of round-trips needed to perform
-        authentication.
-
-10. Support HTTPS (**Medium**)
-    -   With option to only allow authentication through HTTPS
-
-11. Enable OpenID 1.1 compatibility (**Medium**)
-    -   mod\_openid is designed from the OpenID 2.0 specification, which
-        has an OpenID 1.1 compatibility mode.
-
-12. Check specification compliance (**Medium**)
-    -   Walk through the code and make sure it complies with the OpenID
-        specification. Comment code as necessary with the relevant
-        sections in the specification.
-
-Once all these steps are done, mod\_openid could be considered to have
-reached "beta" status and ready to real world use. The following are
-features that would be nice to have in a stable release:
-
-1.  Allow users to always trust realms (***Hard***)
-2.  Allow users to remain logged in with a cookie (***Hard***)
-3.  Enable simple registration using a user's vCard (**Medium**)
-4.  More useful user identity page (***Hard***)
-    -   Allow users to alter what realms they trust and what simple
-        registration information gets sent to relaying parties by
-        default.
-
-5.  OpenID Bot (***Hard***)
-    -   Offers all functionality of the user identity page management
-
-6.  Better designed pages (*Easy*)
-    -   Use semantic XHTML and CSS to allow for custom styling.
-    -   Use the Prosody favicon.
-
-Useful Links
-============
-
--   [OpenID Specifications](http://openid.net/developers/specs/)
--   [OpenID on Wikipedia](http://en.wikipedia.org/wiki/OpenID)
--- a/mod_openid/mod_openid.lua	Thu Feb 13 12:20:46 2025 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,448 +0,0 @@
-local usermanager = require "core.usermanager"
-local httpserver = require "net.httpserver"
-local jidutil = require "util.jid"
-local hmac = require "hmac"
-
-local base64 = require "util.encodings".base64
-
-local humane = require "util.serialization".serialize
-
--- Configuration
-local base = "openid"
-local openidns = "http://specs.openid.net/auth/2.0" -- [#4.1.2]
-local response_404 = { status = "404 Not Found", body = "<h1>Page Not Found</h1>Sorry, we couldn't find what you were looking for :(" };
-
-local associations = {}
-
-local function genkey(length)
-    -- FIXME not cryptographically secure
-    str = {}
-
-    for i = 1,length do
-        local rand = math.random(33, 126)
-        table.insert(str, string.char(rand))
-    end
-
-    return table.concat(str)
-end
-
-local function tokvstring(dict)
-    -- key-value encoding for a dictionary [#4.1.3]
-    local str = ""
-
-    for k,v in pairs(dict) do
-        str = str..k..":"..v.."\n"
-    end
-
-    return str
-end
-
-local function newassoc(key, shared)
-    -- TODO don't use genkey here
-    local handle = genkey(16)
-    associations[handle] = {}
-    associations[handle]["key"] = key
-    associations[handle]["shared"] = shared
-    associations[handle]["time"] = os.time()
-    return handle
-end
-
-local function split(str, sep)
-    local splits = {}
-    str:gsub("([^.."..sep.."]*)"..sep, function(c) table.insert(splits, c) end)
-    return splits
-end
-
-local function sign(response, key)
-    local fields = {}
-
-    for _,field in pairs(split(response["openid.signed"],",")) do
-       fields[field] = response["openid."..field]
-    end
-
-    -- [#10.1]
-    return base64.encode(hmac.sha256(key, tokvstring(fields)))
-end
-
-local function urlencode(s)
-    return (string.gsub(s, "%W",
-        function(str)
-            return string.format("%%%02X", string.byte(str))
-        end))
-end
-
-local function urldecode(s)
-    return(string.gsub(string.gsub(s, "+", " "), "%%(%x%x)",
-        function(str)
-            return string.char(tonumber(str,16))
-        end))
-end
-
-local function utctime()
-    local now = os.time()
-    local diff = os.difftime(now, os.time(os.date("!*t", now)))
-    return now-diff
-end
-
-local function nonce()
-    -- generate a response nonce [#10.1]
-    local random = ""
-    for i=0,10 do
-        random = random..string.char(math.random(33,126))
-    end
-
-    local timestamp = os.date("%Y-%m-%dT%H:%M:%SZ", utctime())
-
-    return timestamp..random
-end
-
-local function query_params(query)
-    if type(query) == "string" and #query > 0 then
-        if query:match("=") then
-            local params = {}
-            for k, v in query:gmatch("&?([^=%?]+)=([^&%?]+)&?") do
-                if k and v then
-                    params[urldecode(k)] = urldecode(v)
-                end
-            end
-            return params
-        else
-            return urldecode(query)
-        end
-    end
-end
-
-local function split_host_port(combined)
-    local host = combined
-    local port = ""
-    local cpos = string.find(combined, ":")
-    if cpos ~= nil then
-        host = string.sub(combined, 0, cpos-1)
-        port = string.sub(combined, cpos+1)
-    end
-
-    return host, port
-end
-
-local function toquerystring(dict)
-    -- query string encoding for a dictionary [#4.1.3]
-    local str = ""
-
-    for k,v in pairs(dict) do
-        str = str..urlencode(k).."="..urlencode(v).."&"
-    end
-
-    return string.sub(str, 0, -1)
-end
-
-local function match_realm(url, realm)
-    -- FIXME do actual match [#9.2]
-    return true
-end
-
-local function handle_endpoint(method, body, request)
-    module:log("debug", "Request at OpenID provider endpoint")
-
-    local params = nil
-
-    if method == "GET" then
-        params = query_params(request.url.query)
-    elseif method == "POST" then
-        params = query_params(body)
-    else
-        -- TODO error
-        return response_404
-    end
-
-    module:log("debug", "Request Parameters:\n"..humane(params))
-
-    if params["openid.ns"] == openidns then
-        -- OpenID 2.0 request [#5.1.1]
-        if params["openid.mode"] == "associate" then
-            -- Associate mode [#8]
-            -- TODO implement association
-
-            -- Error response [#8.2.4]
-            local openidresponse = {
-               ["ns"] = openidns,
-               ["session_type"] = params["openid.session_type"],
-               ["assoc_type"] = params["openid.assoc_type"],
-               ["error"] = "Association not supported... yet",
-               ["error_code"] = "unsupported-type",
-            }
-
-            local kvresponse = tokvstring(openidresponse)
-            module:log("debug", "OpenID Response:\n"..kvresponse)
-            return {
-                headers = {
-                    ["Content-Type"] = "text/plain"
-                },
-                body = kvresponse
-            }
-        elseif params["openid.mode"] == "checkid_setup" or params["openid.mode"] == "checkid_immediate" then
-            -- Requesting authentication [#9]
-            if not params["openid.realm"] then
-                -- set realm to default value of return_to [#9.1]
-                if params["openid.return_to"] then
-                    params["openid.realm"] = params["openid.return_to"]
-                else
-                    -- neither was sent, error [#9.1]
-                    -- FIXME return proper error
-                    return response_404
-                end
-            end
-
-            if params["openid.return_to"] then
-                -- Assure that the return_to url matches the realm [#9.2]
-                if not match_realm(params["openid.return_to"], params["openid.realm"]) then
-                    -- FIXME return proper error
-                    return response_404
-                end
-
-                -- Verify the return url [#9.2.1]
-                -- TODO implement return url verification
-            end
-
-            if params["openid.claimed_id"] and params["openid.identity"] then
-                -- asserting an identifier [#9.1]
-
-                if params["openid.identity"] == "http://specs.openid.net/auth/2.0/identifier_select" then
-                    -- automatically select an identity [#9.1]
-                    params["openid.identity"] = params["openid.claimed_id"]
-                end
-
-                if params["openid.mode"] == "checkid_setup" then
-                    -- Check ID Setup mode
-                    -- TODO implement: NEXT STEP
-                    local head = "<title>Prosody OpenID : Login</title>"
-                    local body = string.format([[
-<p>Open ID Authentication<p>
-<p>Identifier: <tt>%s</tt></p>
-<p>Realm: <tt>%s</tt></p>
-<p>Return: <tt>%s</tt></p>
-<form method="POST" action="%s">
-    Jabber ID: <input type="text" name="jid"/><br/>
-    Password: <input type="password" name="password"/><br/>
-    <input type="hidden" name="openid.return_to" value="%s"/>
-    <input type="submit" value="Authenticate"/>
-</form>
-                    ]], params["openid.claimed_id"], params["openid.realm"], params["openid.return_to"], base, params["openid.return_to"])
-
-                    return string.format([[
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
-%s
-</head>
-<body>
-%s
-</body>
-</html>
-                    ]], head, body)
-                elseif params["openid.mode"] == "checkid_immediate" then
-                    -- Check ID Immediate mode [#9.3]
-                    -- TODO implement check id immediate
-                end
-            else
-                -- not asserting an identifier [#9.1]
-                -- used for extensions
-                -- TODO implement common extensions
-            end
-        elseif params["openid.mode"] == "check_authentication" then
-            module:log("debug", "OpenID Check Authentication Mode")
-            local assoc = associations[params["openid.assoc_handle"]]
-            module:log("debug", "Checking Association Handle: "..params["openid.assoc_handle"])
-            if assoc and not assoc["shared"] then
-                module:log("debug", "Found valid association")
-                local sig = sign(params, assoc["key"])
-
-                local is_valid = "false"
-                if sig == params["openid.sig"] then
-                    is_valid = "true"
-                end
-
-                module:log("debug", "Signature is: "..is_valid)
-
-                openidresponse = {
-                    ns = openidns,
-                    is_valid = is_valid,
-                }
-
-                -- Delete this association
-                associations[params["openid.assoc_handle"]] = nil
-                return {
-                    headers = {
-                        ["Content-Type"] = "text/plain"
-                    },
-                    body = tokvstring(openidresponse),
-                }
-            else
-                module:log("debug", "No valid association")
-                -- TODO return error
-                -- Invalidate the handle [#11.4.2.2]
-            end
-        else
-            -- Some other mode
-            -- TODO error
-        end
-    elseif params["password"] then
-        -- User is authenticating
-        local user, domain = jidutil.split(params["jid"])
-        module:log("debug", "Authenticating "..params["jid"].." ("..user..","..domain..") with password: "..params["password"])
-        local valid = usermanager.validate_credentials(domain, user, params["password"], "PLAIN")
-        if valid then
-            module:log("debug", "Authentication Succeeded: "..params["jid"])
-            if params["openid.return_to"] ~= "" then
-                -- TODO redirect the user to return_to with the openid response
-                -- included, need to handle the case if its a GET, that there are
-                -- existing query parameters on the return_to URL [#10.1]
-                local host, port = split_host_port(request.headers.host)
-                local endpointurl = ""
-                if port == '' then
-                    endpointurl = string.format("http://%s/%s", host, base)
-                else
-                    endpointurl = string.format("http://%s:%s/%s", host, port, base)
-                end
-
-                local nonce = nonce()
-                local key = genkey(32)
-                local assoc_handle = newassoc(key)
-
-                local openidresponse = {
-                    ["openid.ns"] = openidns,
-                    ["openid.mode"] = "id_res",
-                    ["openid.op_endpoint"] = endpointurl,
-                    ["openid.claimed_id"] = endpointurl.."/"..user,
-                    ["openid.identity"] = endpointurl.."/"..user,
-                    ["openid.return_to"] = params["openid.return_to"],
-                    ["openid.response_nonce"] = nonce,
-                    ["openid.assoc_handle"] = assoc_handle,
-                    ["openid.signed"] = "op_endpoint,identity,claimed_id,return_to,assoc_handle,response_nonce", -- FIXME
-                    ["openid.sig"] = nil,
-                }
-
-                openidresponse["openid.sig"] = sign(openidresponse, key)
-
-                queryresponse = toquerystring(openidresponse)
-
-                redirecturl = params["openid.return_to"]
-                -- add the parameters to the return_to
-                if redirecturl:match("?") then
-                    redirecturl = redirecturl.."&"
-                else
-                    redirecturl = redirecturl.."?"
-                end
-
-                redirecturl = redirecturl..queryresponse
-
-                module:log("debug", "Open ID Positive Assertion Response Table:\n"..humane(openidresponse))
-                module:log("debug", "Open ID Positive Assertion Response URL:\n"..queryresponse)
-                module:log("debug", "Redirecting User to:\n"..redirecturl)
-                return {
-                    status = "303 See Other",
-                    headers = {
-                        Location = redirecturl,
-                    },
-                    body = "Redirecting to: "..redirecturl -- TODO Include a note with a hyperlink to redirect
-                }
-            else
-                -- TODO Do something useful is there is no return_to
-            end
-        else
-            module:log("debug", "Authentication Failed: "..params["jid"])
-            -- TODO let them try again
-        end
-    else
-        -- Not an Open ID request, do something useful
-        -- TODO
-    end
-
-    return response_404
-end
-
-local function handle_identifier(method, body, request, id)
-    module:log("debug", "Request at OpenID identifier")
-    local host, port = split_host_port(request.headers.host)
-
-    local user_name = ""
-    local user_domain = ""
-    local apos = string.find(id, "@")
-    if apos == nil then
-        user_name = id
-        user_domain = host
-    else
-        user_name = string.sub(id, 0, apos-1)
-        user_domain = string.sub(id, apos+1)
-    end
-
-    user, domain = jidutil.split(id)
-
-    local exists = usermanager.user_exists(user_name, user_domain)
-
-    if not exists then
-        return response_404
-    end
-
-    local endpointurl = ""
-    if port == '' then
-        endpointurl = string.format("http://%s/%s", host, base)
-    else
-        endpointurl = string.format("http://%s:%s/%s", host, port, base)
-    end
-
-    local head = string.format("<title>Prosody OpenID : %s@%s</title>", user_name, user_domain)
-    -- OpenID HTML discovery [#7.3]
-    head = head .. string.format('<link rel="openid2.provider" href="%s" />', endpointurl)
-
-    local content = 'request.url.path: ' .. request.url.path .. '<br/>'
-    content = content .. 'host+port: ' .. request.headers.host .. '<br/>'
-    content = content .. 'host: ' .. tostring(host) .. '<br/>'
-    content = content .. 'port: ' .. tostring(port) .. '<br/>'
-    content = content .. 'user_name: ' .. user_name .. '<br/>'
-    content = content .. 'user_domain: ' .. user_domain .. '<br/>'
-    content = content .. 'exists: ' .. tostring(exists) .. '<br/>'
-
-    local body = string.format('<p>%s</p>', content)
-
-    local data = string.format([[
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
-%s
-</head>
-<body>
-%s
-</body>
-</html>
-    ]], head, body)
-    return data;
-end
-
-local function handle_request(method, body, request)
-    module:log("debug", "Received request")
-
-    -- Make sure the host is enabled
-    local host = split_host_port(request.headers.host)
-    if not hosts[host] then
-        return response_404
-    end
-
-    if request.url.path == "/"..base then
-        -- OpenID Provider Endpoint
-        return handle_endpoint(method, body, request)
-    else
-        local id = request.url.path:match("^/"..base.."/(.+)$")
-        if id then
-            -- OpenID Identifier
-            return handle_identifier(method, body, request, id)
-        else
-            return response_404
-        end
-    end
-end
-
-httpserver.new{ port = 5280, base = base, handler = handle_request, ssl = false}