view mod_pubsub_post/mod_pubsub_post.lua @ 3015:338b7c808ecc

mod_pubsub_post: Add support for posting Atom feeds, publishing each entry
author Kim Alvefur <zash@zash.se>
date Sun, 20 May 2018 00:04:41 +0200
parents 72dbc9b66de8
children 3f4e2340bfdc
line wrap: on
line source

module:depends("http");

local st = require "util.stanza";
local json = require "util.json";
local xml = require "util.xml";
local uuid_generate = require "util.uuid".generate;
local timestamp_generate = require "util.datetime".datetime;

local pubsub_service = module:depends("pubsub").service;

local error_mapping = {
	["forbidden"] = 403;
	["item-not-found"] = 404;
	["internal-server-error"] = 500;
	["conflict"] = 409;
};

local function publish_payload(node, item_id, payload)
	local post_item = st.stanza("item", { xmlns = "http://jabber.org/protocol/pubsub", id = item_id, })
		:add_child(payload);
	local ok, err = pubsub_service:publish(node, true, item_id, post_item);
	module:log("debug", ":publish(%q, true, %q, %s) -> %q", node, item_id, payload:top_tag(), err or "");
	if not ok then
		return error_mapping[err] or 500;
	end
	return 202;
end

local function publish_atom(node, feed)
	for entry in feed:childtags("entry") do
		local item_id = entry:get_child_text("id");
		if not item_id then
			item_id = uuid_generate();
			entry:tag("id"):text(item_id):up();
		end
		if not entry:get_child_text("published") then
			entry:tag("published"):text(timestamp_generate()):up();
		end
		local resp = publish_payload(node, item_id, entry);
		if resp ~= 202 then return resp; end
	end
	return 202;
end

local function handle_xml(node, payload)
	local xmlpayload, err = xml.parse(payload);
	if not xmlpayload then
		module:log("debug", "XML parse error: %s\n%q", err, payload);
		return { status_code = 400, body = tostring(err) };
	end
	if xmlpayload.attr.xmlns == "http://www.w3.org/2005/Atom" and xmlpayload.name == "feed" then
		return publish_atom(node, xmlpayload);
	else
		return publish_payload(node, "current", xmlpayload);
	end
end

function handle_POST(event, path)
	local request = event.request;
	module:log("debug", "Handling POST: \n%s\n", tostring(request.body));

	local content_type = request.headers.content_type or "application/octet-stream";

	if content_type == "application/xml" or content_type:sub(-4) == "+xml" then
		return handle_xml(path, request.body);
	end

	module:log("debug", "Unsupported content-type: %q", content_type);
	return 415;
end

module:provides("http", {
	route = {
		["POST /*"] = handle_POST;
	};
});

function module.load()
	module:log("debug", "Loaded at %s", module:http_url());
end