Mercurial > prosody-modules
comparison mod_rest/mod_rest.lua @ 3795:f51308fcba83
mod_rest: Allow specifying a webhook/callback to handle incoming stanzas
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Mon, 30 Dec 2019 04:07:25 +0100 |
| parents | 4b258329e6e4 |
| children | d1ad10b76b00 |
comparison
equal
deleted
inserted
replaced
| 3794:4b258329e6e4 | 3795:f51308fcba83 |
|---|---|
| 3 -- Copyright (c) 2019 Kim Alvefur | 3 -- Copyright (c) 2019 Kim Alvefur |
| 4 -- | 4 -- |
| 5 -- This file is MIT/X11 licensed. | 5 -- This file is MIT/X11 licensed. |
| 6 | 6 |
| 7 local errors = require "util.error"; | 7 local errors = require "util.error"; |
| 8 local http = require "net.http"; | |
| 8 local id = require "util.id"; | 9 local id = require "util.id"; |
| 9 local jid = require "util.jid"; | 10 local jid = require "util.jid"; |
| 11 local st = require "util.stanza"; | |
| 10 local xml = require "util.xml"; | 12 local xml = require "util.xml"; |
| 11 | 13 |
| 12 local allow_any_source = module:get_host_type() == "component"; | 14 local allow_any_source = module:get_host_type() == "component"; |
| 13 local validate_from_addresses = module:get_option_boolean("validate_from_addresses", true); | 15 local validate_from_addresses = module:get_option_boolean("validate_from_addresses", true); |
| 14 | 16 |
| 76 module:provides("http", { | 78 module:provides("http", { |
| 77 route = { | 79 route = { |
| 78 POST = handle_post; | 80 POST = handle_post; |
| 79 }; | 81 }; |
| 80 }); | 82 }); |
| 83 | |
| 84 -- Forward stanzas from XMPP to HTTP and return any reply | |
| 85 local rest_url = module:get_option_string("rest_callback_url", nil); | |
| 86 if rest_url then | |
| 87 | |
| 88 local function handle_stanza(event) | |
| 89 local stanza, origin = event.stanza, event.origin; | |
| 90 local reply_needed = stanza.name == "iq"; | |
| 91 | |
| 92 http.request(rest_url, { | |
| 93 body = tostring(stanza), | |
| 94 headers = { | |
| 95 ["Content-Type"] = "application/xmpp+xml", | |
| 96 ["Content-Language"] = stanza.attr["xml:lang"], | |
| 97 Accept = "application/xmpp+xml, text/plain", | |
| 98 }, | |
| 99 }, function (body, code, response) | |
| 100 if (code == 202 or code == 204) and not reply_needed then | |
| 101 -- Delivered, no reply | |
| 102 return; | |
| 103 end | |
| 104 local reply, reply_text; | |
| 105 | |
| 106 if response.headers["content-type"] == "application/xmpp+xml" then | |
| 107 local parsed, err = xml.parse(body); | |
| 108 if not parsed then | |
| 109 module:log("warn", "REST callback responded with invalid XML: %s, %q", err, body); | |
| 110 elseif parsed.name ~= stanza.name then | |
| 111 module:log("warn", "REST callback responded with the wrong stanza type, got %s but expected %s", parsed.name, stanza.name); | |
| 112 else | |
| 113 parsed.attr.to, parsed.attr.from = stanza.attr.from, stanza.attr.to; | |
| 114 if parsed.name == "iq" then | |
| 115 parsed.attr.id = stanza.attr.id; | |
| 116 end | |
| 117 reply = parsed; | |
| 118 end | |
| 119 elseif response.headers["content-type"] == "text/plain" then | |
| 120 reply = st.reply(stanza); | |
| 121 if body ~= "" then | |
| 122 reply_text = body; | |
| 123 end | |
| 124 elseif body ~= "" then -- ignore empty body | |
| 125 module:log("debug", "Callback returned response of unhandled type %q", response.headers["content-type"]); | |
| 126 end | |
| 127 | |
| 128 if not reply then | |
| 129 local code_hundreds = code - (code % 100); | |
| 130 if code_hundreds == 200 then | |
| 131 reply = st.reply(stanza); | |
| 132 if stanza.name ~= "iq" then | |
| 133 reply.attr.id = id.medium(); | |
| 134 end | |
| 135 if reply_text and reply.name == "message" then | |
| 136 reply:body(reply_text, { ["xml:lang"] = response.headers["content-language"] }); | |
| 137 end | |
| 138 -- TODO presence/status=body ? | |
| 139 elseif code_hundreds == 400 then | |
| 140 reply = st.error_reply(stanza, "modify", "bad-request", reply_text); | |
| 141 elseif code_hundreds == 500 then | |
| 142 reply = st.error_reply(stanza, "cancel", "internal-server-error", reply_text); | |
| 143 else | |
| 144 reply = st.error_reply(stanza, "cancel", "undefined-condition", reply_text); | |
| 145 end | |
| 146 end | |
| 147 | |
| 148 origin.send(reply); | |
| 149 end); | |
| 150 | |
| 151 return true; | |
| 152 end | |
| 153 | |
| 154 if module:get_host_type() == "component" then | |
| 155 module:hook("iq/bare", handle_stanza, -1); | |
| 156 module:hook("message/bare", handle_stanza, -1); | |
| 157 module:hook("presence/bare", handle_stanza, -1); | |
| 158 module:hook("iq/full", handle_stanza, -1); | |
| 159 module:hook("message/full", handle_stanza, -1); | |
| 160 module:hook("presence/full", handle_stanza, -1); | |
| 161 module:hook("iq/host", handle_stanza, -1); | |
| 162 module:hook("message/host", handle_stanza, -1); | |
| 163 module:hook("presence/host", handle_stanza, -1); | |
| 164 else | |
| 165 -- Don't override everything on normal VirtualHosts | |
| 166 module:hook("iq/host", handle_stanza, -1); | |
| 167 module:hook("message/host", handle_stanza, -1); | |
| 168 module:hook("presence/host", handle_stanza, -1); | |
| 169 end | |
| 170 end |
