comparison mod_pubsub_feed/mod_pubsub_feed.lua @ 403:fc62b26dfdf6

mod_pubsub_feed: Major cleanup, and use newer APIs. (Thanks Maranda)
author Kim Alvefur <zash@zash.se>
date Thu, 25 Aug 2011 01:21:42 +0200
parents c92a37a72b25
children fe4fdba21a23
comparison
equal deleted inserted replaced
402:c92a37a72b25 403:fc62b26dfdf6
24 24
25 local t_insert = table.insert; 25 local t_insert = table.insert;
26 local add_task = require "util.timer".add_task; 26 local add_task = require "util.timer".add_task;
27 local date, time = os.date, os.time; 27 local date, time = os.date, os.time;
28 local dt_parse, dt_datetime = require "util.datetime".parse, require "util.datetime".datetime; 28 local dt_parse, dt_datetime = require "util.datetime".parse, require "util.datetime".datetime;
29 local http = require "net.http"; 29 local uuid = require "util.uuid".generate;
30 local hmac_sha1 = require "util.hmac".sha1;
30 local parse_feed = require "feeds".feed_from_string; 31 local parse_feed = require "feeds".feed_from_string;
31 local st = require "util.stanza"; 32 local st = require "util.stanza";
33
34 local http = require "net.http";
32 local httpserver = require "net.httpserver"; 35 local httpserver = require "net.httpserver";
33 local formencode = require "net.http".formencode; 36 local formdecode = http.formdecode;
34 local dump = require "util.serialization".serialize; 37 local formencode = http.formencode;
35 local uuid = require "util.uuid".generate; 38 local urldecode = http.urldecode;
36 local hmac_sha1 = require "util.hmac".sha1; 39 local urlencode = http.urlencode;
37
38 local urldecode = require "net.http".urldecode;
39 local urlencode = require "net.http".urlencode;
40 local urlparams = --require "net.http".getQueryParams or whatever MattJ names it, FIXME
41 function(s)
42 if not s:match("=") then return urldecode(s); end
43 local r = {}
44 s:gsub("([^=&]*)=([^&]*)", function(k,v)
45 r[ urldecode(k) ] = urldecode(v);
46 return nil
47 end)
48 return r
49 end;
50 40
51 local config = module:get_option("feeds") or { 41 local config = module:get_option("feeds") or {
52 planet_jabber = "http://planet.jabber.org/atom.xml"; 42 planet_jabber = "http://planet.jabber.org/atom.xml";
53 prosody_blog = "http://blog.prosody.im/feed/atom.xml"; 43 prosody_blog = "http://blog.prosody.im/feed/atom.xml";
54 }; 44 };
55 local refresh_interval = module:get_option_number("feed_pull_interval", 15) * 60; 45 local refresh_interval = module:get_option_number("feed_pull_interval", 15) * 60;
56 local use_pubsubhubub = module:get_option_boolean("use_pubsubhubub", true); -- HTTP by default or not? 46 local use_pubsubhubub = module:get_option_boolean("use_pubsubhubub", true); -- HTTP by default or not?
57 local http_hostname = module:get_option_string("pubsubhubub_httphost", module.host); 47 local httphost = module:get_option_string("pubsubhubub_httphost", module.host); -- If module.host IN A doesn't point to this server, use this to override.
58 local feed_list = { } 48 local feed_list = { }
59 for node, url in pairs(config) do 49 for node, url in pairs(config) do
60 feed_list[node] = { url = url; node = node; last_update = 0 }; 50 feed_list[node] = { url = url; node = node; last_update = 0 };
61 end 51 end
62 52 -- TODO module:hook("config-reloaded", above loop);
63 local ports = module:get_option("feeds_ports") or { 5280 }; 53 -- Also, keeping it somewhere persistent in order to avoid duplicated publishes?
64 if not next(ports) then 54
65 ports = { 5280 }; 55 -- Thanks to Maranda for this
66 end 56 local port, base, ssl = 5280, "callback", false;
67 local port_number, base_name, secure; 57 local ports = module:get_option("feeds_ports") or { port = port, base = base, ssl = ssl };
68 for _, opts in ipairs(ports) do 58 -- FIXME If ports isn't a table, this will cause an error
69 if type(opts) == "number" then 59 local _, first_port = next(ports); -- We base the callback URL on the first port config
70 port_number, base_name = opts, "callback"; 60 if first_port then
71 elseif type(opts) == "table" then 61 if type(first_port) == "number" then
72 port_number, base_name, secure = opts.port or 5280, opts.path or "callback", opts.ssl or nil; 62 port = first_port;
73 elseif type(opts) == "string" then 63 elseif type(first_port) == "table" then
74 base_name, port_number = opts, 5280; 64 port, base, ssl =
65 first_port.port or port,
66 first_port.path or base,
67 first_port.ssl or ssl;
68 elseif type(first_port) == "string" then
69 base = first_port;
75 end 70 end
76 end 71 end
77 72
78 local response_codes = { 73 local response_codes = {
79 ["200"] = "OK"; 74 ["200"] = "OK";
100 local feed = parse_feed(item.data); 95 local feed = parse_feed(item.data);
101 module:log("debug", "updating node %s", node); 96 module:log("debug", "updating node %s", node);
102 for _, entry in ipairs(feed) do 97 for _, entry in ipairs(feed) do
103 entry.attr.xmlns = "http://www.w3.org/2005/Atom"; 98 entry.attr.xmlns = "http://www.w3.org/2005/Atom";
104 99
105 local e_published = entry:get_child("published"); 100 local e_published = entry:get_child_text("published");
106 e_published = e_published and e_published:get_text();
107 e_published = e_published and dt_parse(e_published); 101 e_published = e_published and dt_parse(e_published);
108 local e_updated = entry:get_child("updated"); 102 local e_updated = entry:get_child_text("updated");
109 e_updated = e_updated and e_updated:get_text();
110 e_updated = e_updated and dt_parse(e_updated); 103 e_updated = e_updated and dt_parse(e_updated);
111 104
112 local timestamp = e_updated or e_published or nil; 105 local timestamp = e_updated or e_published or nil;
113 --module:log("debug", "timestamp is %s, item.last_update is %s", tostring(timestamp), tostring(item.last_update)); 106 --module:log("debug", "timestamp is %s, item.last_update is %s", tostring(timestamp), tostring(item.last_update));
114 if not timestamp or not item.last_update or timestamp > item.last_update then 107 if not timestamp or not item.last_update or timestamp > item.last_update then
115 local id = entry:get_child("id"); 108 local id = entry:get_child_text("id");
116 id = id and id:get_text() or item.url.."#"..dt_datetime(timestamp); -- Missing id, so make one up 109 id = id or item.url.."#"..dt_datetime(timestamp); -- Missing id, so make one up
117 local xitem = st.stanza("item", { id = id }):add_child(entry); 110 local xitem = st.stanza("item", { id = id }):add_child(entry);
118 -- TODO Put data from /feed into item/source 111 -- TODO Put data from /feed into item/source
119 112
120 module:log("debug", "publishing to %s, id %s", node, id); 113 module:log("debug", "publishing to %s, id %s", node, id);
121 local ok, err = modules.pubsub.service:publish(node, actor, id, xitem); 114 local ok, err = modules.pubsub.service:publish(node, actor, id, xitem);
186 end 179 end
187 180
188 function subscribe(feed) 181 function subscribe(feed)
189 feed.token = uuid(); 182 feed.token = uuid();
190 feed.secret = uuid(); 183 feed.secret = uuid();
191 local _body, body = { 184 local body = formencode{
192 ["hub.callback"] = format_url(secure, http_hostname, port_number, base_name, feed.node); 185 ["hub.callback"] = format_url(ssl, httphost, port, base, feed.node);
193 ["hub.mode"] = "subscribe"; --TODO unsubscribe 186 ["hub.mode"] = "subscribe"; --TODO unsubscribe
194 ["hub.topic"] = feed.url; 187 ["hub.topic"] = feed.url;
195 ["hub.verify"] = "async"; 188 ["hub.verify"] = "async";
196 ["hub.verify_token"] = feed.token; 189 ["hub.verify_token"] = feed.token;
197 ["hub.secret"] = feed.secret; 190 ["hub.secret"] = feed.secret;
198 --["hub.lease_seconds"] = ""; 191 --["hub.lease_seconds"] = "";
199 }, { }; 192 };
200 for name, value in pairs(_body) do
201 t_insert(body, { name = name, value = value });
202 end --FIXME Why do I have to do this?
203 body = formencode(body);
204 193
205 --module:log("debug", "subscription request, body: %s", body); 194 --module:log("debug", "subscription request, body: %s", body);
206 195
207 --FIXME The subscription states and related stuff 196 --FIXME The subscription states and related stuff
208 feed.subscription = "subscribe"; 197 feed.subscription = "subscribe";
214 203
215 function handle_http_request(method, body, request) 204 function handle_http_request(method, body, request)
216 --module:log("debug", "%s request to %s%s with body %s", method, request.url.path, request.url.query and "?" .. request.url.query or "", #body > 0 and body or "empty"); 205 --module:log("debug", "%s request to %s%s with body %s", method, request.url.path, request.url.query and "?" .. request.url.query or "", #body > 0 and body or "empty");
217 local query = request.url.query or {}; 206 local query = request.url.query or {};
218 if query and type(query) == "string" then 207 if query and type(query) == "string" then
219 query = urlparams(query); 208 query = formdecode(query);
220 --module:log("debug", "GET data: %s", dump(query)); 209 --module:log("debug", "GET data: %s", dump(query));
221 end 210 end
222 --module:log("debug", "Headers: %s", dump(request.headers)); 211 --module:log("debug", "Headers: %s", dump(request.headers));
223 212
224 if method == "GET" then 213 if method == "GET" then
268 end 257 end
269 258
270 function init() 259 function init()
271 module:log("debug", "initiating", module.name); 260 module:log("debug", "initiating", module.name);
272 if use_pubsubhubub then 261 if use_pubsubhubub then
273 module:log("debug", "Starting http server on %s", format_url(secure, http_hostname, port_number, base_name, "NODE")); 262 module:log("debug", "Starting http server on %s", format_url(ssl, httphost, port, base, "NODE"));
274 --httpserver.new{ port = port_number, ssl = secure, type = (ssl and "ssl") or "tcp", base = base_name, handler = handle_http_request }
275 httpserver.new_from_config( ports, handle_http_request, { base = "callback" } ); 263 httpserver.new_from_config( ports, handle_http_request, { base = "callback" } );
276 end 264 end
277 add_task(0, refresh_feeds); 265 add_task(0, refresh_feeds);
278 end 266 end
279 267
280 if prosody.start_time then -- already started 268 if prosody.start_time then -- already started
281 init(); 269 init();
282 else 270 else
283 prosody.events.add_handler("server-started", init); 271 module:hook_global("server-started", init);
284 end 272 end