Software /
code /
prosody-modules
Changeset
661:a6c8f252e5fa
mod_post_msg: Update to the new HTTP API
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Mon, 30 Apr 2012 13:08:40 +0200 |
parents | 660:aa3fbb33700d |
children | 662:b3d130e4b3ae |
files | mod_post_msg/mod_post_msg.lua |
diffstat | 1 files changed, 61 insertions(+), 112 deletions(-) [+] |
line wrap: on
line diff
--- a/mod_post_msg/mod_post_msg.lua Sun Apr 29 20:43:40 2012 +0000 +++ b/mod_post_msg/mod_post_msg.lua Mon Apr 30 13:08:40 2012 +0200 @@ -1,124 +1,73 @@ --- 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 - +module:depends"http" 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 formdecode = require "net.http".formdecode; ---COMPAT 0.7 -if not test_password then - local validate_credentials = require "core.usermanager".validate_credentials; - test_password = function(user, host, password) - return validate_credentials(host, user, password) +local function require_valid_user(f) + return function(event, path) + local request = event.request; + local response = event.response; + local headers = request.headers; + if not headers.authorization then + response.headers.www_authenticate = ("Basic realm=%q"):format(module.host.."/"..module.name); + return 401 + end + local from_jid, password = b64_decode(headers.authorization:match"[^ ]*$"):match"([^:]*):(.*)"; + from_jid = jid_prep(from_jid); + if from_jid and password then + local user, host = jid_split(from_jid); + local ok, err = test_password(user, host, password); + if ok and user and host then + module:log("debug", "Authed as %s", from_jid); + return f(event, path, from_jid); + elseif err then + module:log("debug", "User failed authentication: %s", err); + end + end + return 401 end 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 +local function handle_post(event, path, authed_user) + local request = event.request; + local response = event.response; + local headers = request.headers; + + local body_type = headers.content_type; + local to = jid_prep(path); + local message; + if body_type == "text/plain" then + if to and request.body then + message = msg({ to = to, from = authed_user, type = "chat"},request.body); + end + elseif body_type == "application/x-www-form-urlencoded" then + local post_body = formdecode(request.body); + message = msg({ to = post_body.to or to, from = authed_user, + type = post_body.type or "chat"}, post_body.body); + else + return 415; + end + if message and message.attr.to then + module:log("debug", "Sending %s", tostring(message)); + module:send(message); + return 201; + end + return 422; 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 function setup() - local ports = module:get_option("post_msg_ports") or { 5280 }; - require "net.httpserver".new_from_config(ports, handle_request, { base = "msg" }); -end -if prosody.start_time then -- already started - setup(); -else - prosody.events.add_handler("server-started", setup); -end +module:provides("http", { + default_path = "/msg"; + route = { + ["POST /*"] = require_valid_user(handle_post); + OPTIONS = function(e) + local headers = e.response.headers; + headers.allow = "POST"; + headers.accept = "application/x-www-form-urlencoded, text/plain"; + return 200; + end; + } +});