File

mod_post_msg/mod_post_msg.lua @ 234:abcb59ab355c

Add new motd_sequential module. This module lets you define numbered messages shown to each user in order, but only once per user, and persistent across server restarts. Useful for notifying users of added features and changes in an incremental fashion.
author Jeff Mitchell <jeffrey.mitchell@gmail.com>
date Wed, 04 Aug 2010 22:29:51 +0000
parent 231:b78009874ce5
child 320:8d4f3cd41f82
line wrap: on
line source

-- Recive a HTTP POST and relay it
-- By Kim Alvefur <zash@zash.se>
-- Some code borrowed from mod_webpresence
--
-- Example usage:
--     curl http://example.com:5280/msg/user -u me@example.com:mypassword -d "Hello there"
-- This would send a message to user@example.com from me@example.com


local jid_split = require "util.jid".split;
local jid_prep = require "util.jid".prep;
local msg = require "util.stanza".message;
local test_password = require "core.usermanager".test_password;
local b64_decode = require "util.encodings".base64.decode;
local urldecode = require "net.http".urldecode;
local urlparams = --require "net.http".getQueryParams or whatever MattJ names it
function(s)
	if not s:match("=") then return urldecode(s); end
	local r = {}
	s:gsub("([^=&]*)=([^&]*)", function(k,v)
		r[ urldecode(k) ] = urldecode(v);
		return nil
	end)
	return r
end;

local function http_response(code, message, extra_headers)
	local response = {
		status = code .. " " .. message;
		body = message .. "\n"; }
	if extra_headers then response.headers = extra_headers; end
	return response
end

local function handle_request(method, body, request)
	if request.method == "BREW" then return http_response(418, "I'm a teapot"); end
	if request.method ~= "POST" then
		return http_response(405, "Method Not Allowed", {["Allow"] = "POST"}); end

	-- message to?
	local path_jid = request.url.path:match("[^/]+$");
	if not path_jid or not body then return http_response(400, "Bad Request"); end
	local to_user, to_host = jid_split(urldecode(path_jid));
	if to_host and not to_user and request.headers.host then
		to_user, to_host = to_host, request.headers.host;
		if to_host then to_host = to_host:gsub(":%d+$", ""); end
	end
	if not to_host or not to_user then return http_response(400, "Bad Request"); end 
	local to_jid = jid_prep(to_user .. "@" .. to_host)
	if not to_jid then return http_response(400, "Bad Request"); end 

	-- message from?
	if not request.headers["authorization"] then
		return http_response(401, "Unauthorized",
			{["WWW-Authenticate"]='Basic realm="WallyWorld"'})
	end
	local from_jid, password = b64_decode(request.headers.authorization
			:match("[^ ]*$") or ""):match("([^:]*):(.*)");
	from_jid = jid_prep(from_jid)
	if not from_jid or not password then return http_response(400, "Bad Request"); end
	local from_user, from_host = jid_split(from_jid)
	if not hosts[from_host] then return http_response(401, "Unauthorized"); end

	-- auth
	module:log("debug", "testing authz %s", from_jid)
	if not test_password(from_user, from_host, password) then
		return http_response(401, "Unauthorized")
	end

	-- parse body
	local message = {}
	local body_type = request.headers["content-type"]
	if body_type == "text/plain" then
		message = {["body"] = body}
	elseif body_type == "application/x-www-form-urlencoded" then
		message = urlparams(body)
		if type(message) == "string" then
			message = {["body"] = message}
		end
	else
		return http_response(415, "Unsupported Media Type")
	end

	-- guess type if not set
	if not message["type"] then
		if message["body"] then 
			if message["subject"] then
				message["type"] = "normal"
			else
				message["type"] = "chat"
			end
		elseif not message["body"] and message["subject"] then
			message["type"] = "headline"
		end
	end

	-- build stanza
	local stanza = msg({["to"]=to_jid, ["from"]=from_jid, ["type"]=message["type"]})
	if message["body"] then stanza:tag("body"):text(message["body"]):up(); end
	if message["subject"] then stanza:tag("subject"):text(message["subject"]):up(); end

	-- and finaly post it
	module:log("debug", "message for %s", to_jid)
	core_post_stanza(hosts[module.host], stanza)
	return http_response(202, "Accepted")
end

local ports = config.get(module.host, "core", "post_msg_ports") or { 5280 };
require "net.httpserver".new_from_config(ports, "msg", handle_request);