From 7b5aa05b026aadab237d9d101a9f455ea070e5e8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 29 Mar 2017 22:38:31 +0200 Subject: util.id: New util for producing random identifiers of varying sizes --- util/id.lua | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 util/id.lua 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; +} -- cgit v1.2.3 From 72018f7be717db4e99cf175d46cbc9d97c6249aa Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:29:08 +0200 Subject: util.array: Add method for filtering out duplicate values --- util/array.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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]; -- cgit v1.2.3 From 0ca328a00c3ab900d996c33755ef73ca869d153d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:34:33 +0200 Subject: mod_storage_internal: Separate driver from keyval implementation --- plugins/mod_storage_internal.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index ade4f0a6..dd8cf61f 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -3,19 +3,23 @@ local datamanager = require "core.storagemanager".olddm; 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) + +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 @@ -23,7 +27,7 @@ function driver:stores(username) return datamanager.stores(username, host); end -function driver:users() +function keyval:users() return datamanager.users(host, self.store, self.type); end -- cgit v1.2.3 From b1d929678998a3b6e2e31b234bbac244b2379454 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:38:46 +0200 Subject: mod_storage_internal: Reorder methods --- plugins/mod_storage_internal.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index dd8cf61f..557a329e 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -12,6 +12,14 @@ function driver:open(store, typ) return setmetatable({ store = store, type = typ }, mt); end +function driver:stores(username) + return datamanager.stores(username, host); +end + +function driver:purge(user) + return datamanager.purge(user, host); +end + local keyval = { }; driver.keyval = { __index = keyval }; @@ -23,16 +31,8 @@ function keyval:set(user, data) return datamanager.store(user, host, self.store, data); end -function driver:stores(username) - return datamanager.stores(username, host); -end - function keyval:users() return datamanager.users(host, self.store, self.type); end -function driver:purge(user) - return datamanager.purge(user, host); -end - module:provides("storage", driver); -- cgit v1.2.3 From fefbf230135141e84fdb5bb7ba51284715358599 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:39:41 +0200 Subject: mod_storage_internal: Ignore unused 'self' argument [luacheck] --- plugins/mod_storage_internal.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index 557a329e..294d823c 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -12,11 +12,11 @@ function driver:open(store, typ) return setmetatable({ store = store, type = typ }, mt); end -function driver:stores(username) +function driver:stores(username) -- luacheck: ignore 212/self return datamanager.stores(username, host); end -function driver:purge(user) +function driver:purge(user) -- luacheck: ignore 212/self return datamanager.purge(user, host); end -- cgit v1.2.3 From ffb3266efa026dde9275728a06224f2c7814fcba Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:47:06 +0200 Subject: mod_storage_internal: Add basic archive store implementation --- plugins/mod_storage_internal.lua | 85 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index 294d823c..a5401f70 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -1,4 +1,9 @@ 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; @@ -35,4 +40,84 @@ function keyval:users() return datamanager.users(host, self.store, self.type); end +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 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 + module:provides("storage", driver); -- cgit v1.2.3 From 8be8224bbacc9de5cfbb11a566c85aa2eca1f8dc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:48:50 +0200 Subject: mod_storage_internal: Add support for removing archived items --- plugins/mod_storage_internal.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index a5401f70..c1e460eb 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -120,4 +120,23 @@ function archive:find(username, query) end, count; 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); -- cgit v1.2.3 From 52cff8b4905e24bfeabcc088d82c08c09e347f5e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:49:51 +0200 Subject: mod_storage_internal: Add the dates method --- plugins/mod_storage_internal.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index c1e460eb..09760838 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -120,6 +120,12 @@ function archive:find(username, query) 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); -- cgit v1.2.3 From dc72d25c39c545357197cc8c6f8ed0ecee329c07 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 17:50:19 +0200 Subject: mod_offline: Switch to using archive store via the storagemanager API --- plugins/mod_offline.lua | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 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); -- cgit v1.2.3 From d36a5333ea6c3d3d7806450f836dd132848f667b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 31 Mar 2017 18:52:53 +0200 Subject: MUC: Only create rooms in a locked state when they are created by someone joining (fixes timed deletion of all rooms on startup) --- plugins/muc/mod_muc.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 90b01cc7..8c223cb2 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -86,12 +86,12 @@ local function room_save(room, forced) if forced then persistent_rooms_storage:set(nil, persistent_rooms); end end -function create_room(jid) +function create_room(jid, locked) local room = muc_new_room(jid); room.route_stanza = room_route_stanza; room.save = room_save; rooms[jid] = room; - if lock_rooms then + if locked then room.locked = true; if lock_room_timeout and lock_room_timeout > 0 then module:add_timer(lock_room_timeout, function () @@ -166,7 +166,7 @@ function stanza_handler(event) if not(restrict_room_creation) or is_admin(stanza.attr.from) or (restrict_room_creation == "local" and select(2, jid_split(stanza.attr.from)) == module.host:gsub("^[^%.]+%.", "")) then - room = create_room(bare); + room = create_room(bare, lock_rooms); end end if room then -- cgit v1.2.3