Software /
code /
prosody-modules
Comparison
mod_post_msg/mod_post_msg.lua @ 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 |
parent | 321:661f64627fed |
child | 1302:e556219cb43d |
comparison
equal
deleted
inserted
replaced
660:aa3fbb33700d | 661:a6c8f252e5fa |
---|---|
1 -- Recive a HTTP POST and relay it | 1 module:depends"http" |
2 -- By Kim Alvefur <zash@zash.se> | |
3 -- Some code borrowed from mod_webpresence | |
4 -- | |
5 -- Example usage: | |
6 -- curl http://example.com:5280/msg/user -u me@example.com:mypassword -d "Hello there" | |
7 -- This would send a message to user@example.com from me@example.com | |
8 | |
9 | 2 |
10 local jid_split = require "util.jid".split; | 3 local jid_split = require "util.jid".split; |
11 local jid_prep = require "util.jid".prep; | 4 local jid_prep = require "util.jid".prep; |
12 local msg = require "util.stanza".message; | 5 local msg = require "util.stanza".message; |
13 local test_password = require "core.usermanager".test_password; | 6 local test_password = require "core.usermanager".test_password; |
14 local b64_decode = require "util.encodings".base64.decode; | 7 local b64_decode = require "util.encodings".base64.decode; |
15 local urldecode = require "net.http".urldecode; | 8 local formdecode = require "net.http".formdecode; |
16 local urlparams = --require "net.http".getQueryParams or whatever MattJ names it | |
17 function(s) | |
18 if not s:match("=") then return urldecode(s); end | |
19 local r = {} | |
20 s:gsub("([^=&]*)=([^&]*)", function(k,v) | |
21 r[ urldecode(k) ] = urldecode(v); | |
22 return nil | |
23 end) | |
24 return r | |
25 end; | |
26 | 9 |
27 --COMPAT 0.7 | 10 local function require_valid_user(f) |
28 if not test_password then | 11 return function(event, path) |
29 local validate_credentials = require "core.usermanager".validate_credentials; | 12 local request = event.request; |
30 test_password = function(user, host, password) | 13 local response = event.response; |
31 return validate_credentials(host, user, password) | 14 local headers = request.headers; |
15 if not headers.authorization then | |
16 response.headers.www_authenticate = ("Basic realm=%q"):format(module.host.."/"..module.name); | |
17 return 401 | |
18 end | |
19 local from_jid, password = b64_decode(headers.authorization:match"[^ ]*$"):match"([^:]*):(.*)"; | |
20 from_jid = jid_prep(from_jid); | |
21 if from_jid and password then | |
22 local user, host = jid_split(from_jid); | |
23 local ok, err = test_password(user, host, password); | |
24 if ok and user and host then | |
25 module:log("debug", "Authed as %s", from_jid); | |
26 return f(event, path, from_jid); | |
27 elseif err then | |
28 module:log("debug", "User failed authentication: %s", err); | |
29 end | |
30 end | |
31 return 401 | |
32 end | 32 end |
33 end | 33 end |
34 | 34 |
35 local function http_response(code, message, extra_headers) | 35 local function handle_post(event, path, authed_user) |
36 local response = { | 36 local request = event.request; |
37 status = code .. " " .. message; | 37 local response = event.response; |
38 body = message .. "\n"; } | 38 local headers = request.headers; |
39 if extra_headers then response.headers = extra_headers; end | 39 |
40 return response | 40 local body_type = headers.content_type; |
41 local to = jid_prep(path); | |
42 local message; | |
43 if body_type == "text/plain" then | |
44 if to and request.body then | |
45 message = msg({ to = to, from = authed_user, type = "chat"},request.body); | |
46 end | |
47 elseif body_type == "application/x-www-form-urlencoded" then | |
48 local post_body = formdecode(request.body); | |
49 message = msg({ to = post_body.to or to, from = authed_user, | |
50 type = post_body.type or "chat"}, post_body.body); | |
51 else | |
52 return 415; | |
53 end | |
54 if message and message.attr.to then | |
55 module:log("debug", "Sending %s", tostring(message)); | |
56 module:send(message); | |
57 return 201; | |
58 end | |
59 return 422; | |
41 end | 60 end |
42 | 61 |
43 local function handle_request(method, body, request) | 62 module:provides("http", { |
44 if request.method == "BREW" then return http_response(418, "I'm a teapot"); end | 63 default_path = "/msg"; |
45 if request.method ~= "POST" then | 64 route = { |
46 return http_response(405, "Method Not Allowed", {["Allow"] = "POST"}); end | 65 ["POST /*"] = require_valid_user(handle_post); |
47 | 66 OPTIONS = function(e) |
48 -- message to? | 67 local headers = e.response.headers; |
49 local path_jid = request.url.path:match("[^/]+$"); | 68 headers.allow = "POST"; |
50 if not path_jid or not body then return http_response(400, "Bad Request"); end | 69 headers.accept = "application/x-www-form-urlencoded, text/plain"; |
51 local to_user, to_host = jid_split(urldecode(path_jid)); | 70 return 200; |
52 if to_host and not to_user and request.headers.host then | 71 end; |
53 to_user, to_host = to_host, request.headers.host; | 72 } |
54 if to_host then to_host = to_host:gsub(":%d+$", ""); end | 73 }); |
55 end | |
56 if not to_host or not to_user then return http_response(400, "Bad Request"); end | |
57 local to_jid = jid_prep(to_user .. "@" .. to_host) | |
58 if not to_jid then return http_response(400, "Bad Request"); end | |
59 | |
60 -- message from? | |
61 if not request.headers["authorization"] then | |
62 return http_response(401, "Unauthorized", | |
63 {["WWW-Authenticate"]='Basic realm="WallyWorld"'}) | |
64 end | |
65 local from_jid, password = b64_decode(request.headers.authorization | |
66 :match("[^ ]*$") or ""):match("([^:]*):(.*)"); | |
67 from_jid = jid_prep(from_jid) | |
68 if not from_jid or not password then return http_response(400, "Bad Request"); end | |
69 local from_user, from_host = jid_split(from_jid) | |
70 if not hosts[from_host] then return http_response(401, "Unauthorized"); end | |
71 | |
72 -- auth | |
73 module:log("debug", "testing authz %s", from_jid) | |
74 if not test_password(from_user, from_host, password) then | |
75 return http_response(401, "Unauthorized") | |
76 end | |
77 | |
78 -- parse body | |
79 local message = {} | |
80 local body_type = request.headers["content-type"] | |
81 if body_type == "text/plain" then | |
82 message = {["body"] = body} | |
83 elseif body_type == "application/x-www-form-urlencoded" then | |
84 message = urlparams(body) | |
85 if type(message) == "string" then | |
86 message = {["body"] = message} | |
87 end | |
88 else | |
89 return http_response(415, "Unsupported Media Type") | |
90 end | |
91 | |
92 -- guess type if not set | |
93 if not message["type"] then | |
94 if message["body"] then | |
95 if message["subject"] then | |
96 message["type"] = "normal" | |
97 else | |
98 message["type"] = "chat" | |
99 end | |
100 elseif not message["body"] and message["subject"] then | |
101 message["type"] = "headline" | |
102 end | |
103 end | |
104 | |
105 -- build stanza | |
106 local stanza = msg({["to"]=to_jid, ["from"]=from_jid, ["type"]=message["type"]}) | |
107 if message["body"] then stanza:tag("body"):text(message["body"]):up(); end | |
108 if message["subject"] then stanza:tag("subject"):text(message["subject"]):up(); end | |
109 | |
110 -- and finaly post it | |
111 module:log("debug", "message for %s", to_jid) | |
112 core_post_stanza(hosts[module.host], stanza) | |
113 return http_response(202, "Accepted") | |
114 end | |
115 | |
116 local function setup() | |
117 local ports = module:get_option("post_msg_ports") or { 5280 }; | |
118 require "net.httpserver".new_from_config(ports, handle_request, { base = "msg" }); | |
119 end | |
120 if prosody.start_time then -- already started | |
121 setup(); | |
122 else | |
123 prosody.events.add_handler("server-started", setup); | |
124 end |