mod_log_ringbuffer: Detach event handlers on logging reload (thanks Menel)
Otherwise the global event handlers accumulate, one added each time
logging is reoladed, and each invocation of the signal or event triggers
one dump of each created ringbuffer.
module:depends("http");
local st = require "util.stanza";
local json = require "util.json";
local hashes = require "util.hashes";
local from_hex = require "util.hex".from;
local hmacs = {
sha1 = hashes.hmac_sha1;
sha256 = hashes.hmac_sha256;
sha384 = hashes.hmac_sha384;
sha512 = hashes.hmac_sha512;
};
local pubsub_service = module:depends("pubsub").service;
-- configuration
local default_node = module:get_option("github_node", "github");
local node_prefix = module:get_option_string("github_node_prefix", "github/");
local node_mapping = module:get_option_string("github_node_mapping");
local github_actor = module:get_option_string("github_actor") or true;
local github_secret = module:get_option("github_secret");
-- validation
assert(github_secret, "Please set 'github_secret'");
local error_mapping = {
["forbidden"] = 403;
["item-not-found"] = 404;
["internal-server-error"] = 500;
["conflict"] = 409;
};
local function verify_signature(secret, body, signature)
if not signature then return false; end
local algo, digest = signature:match("^([^=]+)=(%x+)");
if not algo then return false; end
local hmac = hmacs[algo];
if not algo then return false; end
return hmac(secret, body) == from_hex(digest);
end
function handle_POST(event)
local request, response = event.request, event.response;
if not verify_signature(github_secret, request.body, request.headers.x_hub_signature) then
module:log("debug", "Signature validation failed");
return 401;
end
local data = json.decode(request.body);
if not data then
response.status_code = 400;
return "Invalid JSON. From you of all people...";
end
local node = default_node;
if node_mapping then
node = node_prefix .. data.repository[node_mapping];
end
local github_event = request.headers.x_github_event or data.object_kind;
if not github_event and data.commits then
github_event = "push"; -- curl?
end
module:log("debug", "Handling '%s' event: \n%s\n", github_event, tostring(request.body));
if github_event == "push" then
for _, commit in ipairs(data.commits) do
local ok, err = pubsub_service:publish(node, github_actor, commit.id,
st.stanza("item", { id = commit.id, xmlns = "http://jabber.org/protocol/pubsub" })
:tag("entry", { xmlns = "http://www.w3.org/2005/Atom" })
:tag("id"):text(commit.id):up()
:tag("title"):text(commit.message:match("^[^\r\n]*")):up()
:tag("summary"):text(("Commit to %s by %s: %s"):format(data.repository.name, commit.author.name, commit.message:match("^[^\r\n]*"))):up()
:tag("content"):text(commit.message):up()
:tag("link", { rel = "alternate", href = commit.url }):up()
:tag("published"):text(commit.author.date):up()
:tag("author")
:tag("name"):text(commit.author.name):up()
:tag("email"):text(commit.author.email):up()
:up()
);
if not ok then
return error_mapping[err] or 500;
end
end
elseif github_event then
module:log("debug", "Unsupported Github event %q", github_event);
return 501;
end
response.status_code = 202;
return "Thank you Github!";
end
module:provides("http", {
route = {
POST = handle_POST;
};
});
if not node_mapping then
function module.load()
if not pubsub_service.nodes[default_node] then
local ok, err = pubsub_service:create(default_node, true);
if not ok then
module:log("error", "Error creating node: %s", err);
else
module:log("debug", "Node %q created", default_node);
end
end
end
end