--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_storage_muc_log/mod_storage_muc_log.lua Mon Nov 10 14:41:01 2014 +0100
@@ -0,0 +1,154 @@
+
+local datamanager = require"core.storagemanager".olddm;
+local xml_parse = require"util.xml".parse;
+local data_load, data_store = datamanager.load, datamanager.store;
+local datastore = "muc_log";
+local datetime = require"util.datetime"
+local lfs = require"lfs";
+local noop = function () end;
+local os_date = os.date;
+
+local timef, datef = "!%X", "!%y%m%d";
+local host = module.host;
+
+local driver = {};
+local driver_mt = { __index = driver };
+
+do
+ -- Sanity check
+ -- Fun fact: 09:00 and 21:00 en_HK are both "09:00:00 UTC"
+ local t = os_date("!*t");
+ t.hour = 9;
+ local am = os_date(timef, os.time(t));
+ t.hour = 21;
+ local pm = os_date(timef, os.time(t));
+ if am == pm then
+ module:log("warn", "Timestamps in AM and PM are identical in your locale, expect timestamps to be wrong");
+ end
+end
+
+local function parse_silly(date, time)
+ local year, month, day = date:match("^(%d%d)(%d%d)(%d%d)");
+ year = "20"..year;
+ -- year = (year < "70" and "20" or "19") .. year;
+ local hour, min, sec = time:match("(%d%d)%D+(%d%d)%D+(%d%d)");
+ if hour == "12" and time:find("[Aa][Mm]") then
+ hour = "00";
+ elseif hour < "12" and time:find("[Pp][Mm]") then
+ hour = tostring(tonumber(hour) % 12 + 12);
+ end
+ return datetime.parse(("%s-%s-%sT%s:%s:%sZ"):format(year, month, day, hour or "00", min or "00", sec or "00"));
+end
+
+local function st_with(tag)
+ local with = tag.attr.type;
+ return with and tag.name .. "<" .. with or tag.name;
+end
+
+function driver:append(node, key, when, with, stanza)
+ local today = os_date(datef, when);
+ local now = os_date(timef, when);
+ local data = data_load(node, host, datastore .. "/" .. today) or {};
+ data[#data + 1] = "<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n";
+ datamanager.getpath(node, host, datastore, nil, true); -- create the datastore dir
+ local ok, err = data_store(node, host, datastore .. "/" .. today, data);
+ if not ok then
+ return ok, err;
+ end
+ return today .. "_" .. #data;
+end
+
+function driver:find(node, query)
+ local path = datamanager.getpath(node, host, datastore):match("(.*)/");
+
+ local ok, iter, state, var = pcall(lfs.dir, path);
+ if not ok then
+ module:log("warn", iter);
+ return nil, iter;
+ end
+
+ local dates, i = {}, 1;
+ for dir in iter, state, var do
+ if lfs.attributes(datamanager.getpath(node, host, datastore .. "/" .. dir), "mode") == "file" then
+ dates[i], i = dir, i+1;
+ end
+ end
+ if dates[1] == nil then return noop, 0; end
+ table.sort(dates);
+
+ return coroutine.wrap(function ()
+ local query = query;
+ local dates = dates;
+ local start_date = query and query.start and os_date(datef, query.start) or dates[1];
+ local end_date = query and query["end"] and os_date(datef, query["end"]) or dates[#dates];
+ local start_time = query and query.start and os_date(timef, query.start) or dates[1];
+ local end_time = query and query["end"] and os_date(timef, query["end"]) or dates[#dates];
+ local query_with = query and query.with;
+ local query_limit = query and query.limit;
+ local seek_once = query and query.after;
+
+ local today, time, data, err, item;
+ local inner_start, inner_stop, inner_step;
+ local outer_start, outer_stop, outer_step = 1, #dates, 1;
+ if query and query.reverse then
+ outer_start, outer_stop, outer_step = outer_stop, outer_start, -outer_step;
+ seek_once = query.before;
+ if seek_once then
+ end_date = seek_once:match"^(%d+)_%d";
+ end
+ elseif seek_once then
+ start_date = seek_once:match"^(%d+)_%d";
+ end
+ local matches = 0;
+ for i = outer_start, outer_stop, outer_step do
+ today = dates[i];
+ if today >= start_date and today <= end_date then
+ data, err = data_load(node, host, datastore .. "/" .. today);
+ if data then
+ inner_start, inner_stop, inner_step = 1, #data, 1;
+ if query and query.reverse then
+ inner_start, inner_stop, inner_step = inner_stop, inner_start, -inner_step;
+ end
+ if seek_once then
+ inner_start = tonumber(seek_once:match("_(%d+)$"));
+ inner_start = inner_start + (query and query.reverse and -1 or 1);
+ seek_once = nil;
+ end
+ for i = inner_start, inner_stop, inner_step do
+ item, err = data[i];
+ if item then
+ item, err = xml_parse(item);
+ end
+ if item then
+ time = item.attr.time;
+ item = item.tags[1];
+ if (today >= start_date or time >= start_time) and
+ (today <= end_date or time <= end_time) and
+ (not query_with or query_with == st_with(item)) and
+ item:get_child_text("alreadyJoined") ~= "true" then
+ matches = matches + 1;
+ coroutine.yield(today.."_"..i, item, parse_silly(today, time));
+ if query_limit and matches >= query_limit then
+ return;
+ end
+ end
+ elseif err then
+ module:log("warn", err);
+ end
+ end
+ elseif err then
+ module:log("warn", err);
+ end
+ end
+ end
+ end);
+end
+
+function open(_, store, typ)
+ if typ ~= "archive" then
+ return nil, "unsupported-store";
+ end
+ return setmetatable({ store = store, type = typ }, driver_mt);
+end
+
+module:provides "storage";