aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Alvefur <zash@zash.se>2017-03-31 19:01:42 +0200
committerKim Alvefur <zash@zash.se>2017-03-31 19:01:42 +0200
commit7eb36daf82fa0db2544118d950bed17b0c6faa93 (patch)
treebee3cc0bc75de8d1aa4fb7ea7ca6571c449ac139
parentf389cff7eaeff6492874a0808bb8bf1ec9fb78fd (diff)
parentd36a5333ea6c3d3d7806450f836dd132848f667b (diff)
downloadprosody-7eb36daf82fa0db2544118d950bed17b0c6faa93.tar.gz
prosody-7eb36daf82fa0db2544118d950bed17b0c6faa93.zip
Merge 0.10->trunk
-rw-r--r--plugins/mod_offline.lua28
-rw-r--r--plugins/mod_storage_internal.lua136
-rw-r--r--util/array.lua12
-rw-r--r--util/id.lua26
4 files changed, 173 insertions, 29 deletions
diff --git a/plugins/mod_offline.lua b/plugins/mod_offline.lua
index 08ab8490..fe4df573 100644
--- a/plugins/mod_offline.lua
+++ b/plugins/mod_offline.lua
@@ -7,29 +7,24 @@
--
-local datamanager = require "util.datamanager";
-local st = require "util.stanza";
local datetime = require "util.datetime";
-local ipairs = ipairs;
local jid_split = require "util.jid".split;
+local offline_messages = module:open_store("offline", "archive");
+
module:add_feature("msgoffline");
module:hook("message/offline/handle", function(event)
local origin, stanza = event.origin, event.stanza;
local to = stanza.attr.to;
- local node, host;
+ local node;
if to then
- node, host = jid_split(to)
+ node = jid_split(to)
else
- node, host = origin.username, origin.host;
+ node = origin.username;
end
- stanza.attr.stamp, stanza.attr.stamp_legacy = datetime.datetime(), datetime.legacy();
- local result = datamanager.list_append(node, host, "offline", st.preserialize(stanza));
- stanza.attr.stamp, stanza.attr.stamp_legacy = nil, nil;
-
- return result;
+ return offline_messages:append(node, nil, stanza);
end, -1);
module:hook("message/offline/broadcast", function(event)
@@ -37,15 +32,12 @@ module:hook("message/offline/broadcast", function(event)
local node, host = origin.username, origin.host;
- local data = datamanager.list_load(node, host, "offline");
+ local data = offline_messages:find(node);
if not data then return true; end
- for _, stanza in ipairs(data) do
- stanza = st.deserialize(stanza);
- stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = host, stamp = stanza.attr.stamp}):up(); -- XEP-0203
- stanza:tag("x", {xmlns = "jabber:x:delay", from = host, stamp = stanza.attr.stamp_legacy}):up(); -- XEP-0091 (deprecated)
- stanza.attr.stamp, stanza.attr.stamp_legacy = nil, nil;
+ for _, stanza, when in data do
+ stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = host, stamp = datetime.datetime(when)}):up(); -- XEP-0203
origin.send(stanza);
end
- datamanager.list_store(node, host, "offline", nil);
+ offline_messages:delete(node);
return true;
end, -1);
diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua
index ade4f0a6..09760838 100644
--- a/plugins/mod_storage_internal.lua
+++ b/plugins/mod_storage_internal.lua
@@ -1,34 +1,148 @@
local datamanager = require "core.storagemanager".olddm;
+local array = require "util.array";
+local datetime = require "util.datetime";
+local st = require "util.stanza";
+local now = require "util.time".now;
+local id = require "util.id".medium;
local host = module.host;
local driver = {};
-local driver_mt = { __index = driver };
function driver:open(store, typ)
- if typ and typ ~= "keyval" then
+ local mt = self[typ or "keyval"]
+ if not mt then
return nil, "unsupported-store";
end
- return setmetatable({ store = store, type = typ }, driver_mt);
+ return setmetatable({ store = store, type = typ }, mt);
end
-function driver:get(user)
+
+function driver:stores(username) -- luacheck: ignore 212/self
+ return datamanager.stores(username, host);
+end
+
+function driver:purge(user) -- luacheck: ignore 212/self
+ return datamanager.purge(user, host);
+end
+
+local keyval = { };
+driver.keyval = { __index = keyval };
+
+function keyval:get(user)
return datamanager.load(user, host, self.store);
end
-function driver:set(user, data)
+function keyval:set(user, data)
return datamanager.store(user, host, self.store, data);
end
-function driver:stores(username)
- return datamanager.stores(username, host);
+function keyval:users()
+ return datamanager.users(host, self.store, self.type);
end
-function driver:users()
- return datamanager.users(host, self.store, self.type);
+local archive = {};
+driver.archive = { __index = archive };
+
+function archive:append(username, key, value, when, with)
+ key = key or id();
+ when = when or now();
+ if not st.is_stanza(value) then
+ return nil, "unsupported-datatype";
+ end
+ value = st.preserialize(st.clone(value));
+ value.key = key;
+ value.when = when;
+ value.with = with;
+ value.attr.stamp = datetime.datetime(when);
+ value.attr.stamp_legacy = datetime.legacy(when);
+ local ok, err = datamanager.list_append(username, host, self.store, value);
+ if not ok then return ok, err; end
+ return key;
end
-function driver:purge(user)
- return datamanager.purge(user, host);
+function archive:find(username, query)
+ local items, err = datamanager.list_load(username, host, self.store);
+ if not items then return items, err; end
+ local count = #items;
+ local i = 0;
+ if query then
+ items = array(items);
+ if query.with then
+ items:filter(function (item)
+ return item.with == query.with;
+ end);
+ end
+ if query.start then
+ items:filter(function (item)
+ return item.when >= query.start;
+ end);
+ end
+ if query["end"] then
+ items:filter(function (item)
+ return item.when <= query["end"];
+ end);
+ end
+ count = #items;
+ if query.reverse then
+ items:reverse();
+ if query.before then
+ for j = 1, count do
+ if (items[j].key or tostring(j)) == query.before then
+ i = j;
+ break;
+ end
+ end
+ end
+ elseif query.after then
+ for j = 1, count do
+ if (items[j].key or tostring(j)) == query.after then
+ i = j;
+ break;
+ end
+ end
+ end
+ if query.limit and #items - i > query.limit then
+ items[i+query.limit+1] = nil;
+ end
+ end
+ return function ()
+ i = i + 1;
+ local item = items[i];
+ if not item then return; end
+ local key = item.key or tostring(i);
+ local when = item.when or datetime.parse(item.attr.stamp);
+ local with = item.with;
+ item.key, item.when, item.with = nil, nil, nil;
+ item.attr.stamp = nil;
+ item.attr.stamp_legacy = nil;
+ item = st.deserialize(item);
+ return key, item, when, with;
+ end, count;
+end
+
+function archive:dates(username)
+ local items, err = datamanager.list_load(username, host, self.store);
+ if not items then return items, err; end
+ return array(items):pluck("when"):map(datetime.date):unique();
+end
+
+function archive:delete(username, query)
+ if not query or next(query) == nil then
+ return datamanager.list_store(username, host, self.store, nil);
+ end
+ for k in pairs(query) do
+ if k ~= "end" then return nil, "unsupported-query-field"; end
+ end
+ local items, err = datamanager.list_load(username, host, self.store);
+ if not items then return items, err; end
+ items = array(items);
+ items:filter(function (item)
+ return item.when > query["end"];
+ end);
+ local count = #items;
+ local ok, err = datamanager.list_store(username, host, self.store, items);
+ if not ok then return ok, err; end
+ return count;
end
module:provides("storage", driver);
diff --git a/util/array.lua b/util/array.lua
index 05fa97ca..150b4355 100644
--- a/util/array.lua
+++ b/util/array.lua
@@ -92,6 +92,18 @@ function array_base.sort(outa, ina, ...)
return outa;
end
+function array_base.unique(outa, ina)
+ local seen = {};
+ return array_base.filter(outa, ina, function (item)
+ if seen[item] then
+ return false;
+ else
+ seen[item] = true;
+ return true;
+ end
+ end);
+end
+
function array_base.pluck(outa, ina, key)
for i = 1, #ina do
outa[i] = ina[i][key];
diff --git a/util/id.lua b/util/id.lua
new file mode 100644
index 00000000..731355fa
--- /dev/null
+++ b/util/id.lua
@@ -0,0 +1,26 @@
+-- Prosody IM
+-- Copyright (C) 2008-2017 Matthew Wild
+-- Copyright (C) 2008-2017 Waqas Hussain
+-- Copyright (C) 2008-2017 Kim Alvefur
+--
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+local s_gsub = string.gsub;
+local random_bytes = require "util.random".bytes;
+local base64_encode = require "util.encodings".base64.encode;
+
+local b64url = { ["+"] = "-", ["/"] = "_", ["="] = "" };
+local function b64url_random(len)
+ return (s_gsub(base64_encode(random_bytes(len)), "[+/=]", b64url));
+end
+
+return {
+ short = function () return b64url_random(6); end;
+ medium = function () return b64url_random(12); end;
+ long = function () return b64url_random(24); end;
+ custom = function (size)
+ return function () return b64url_random(size); end;
+ end;
+}