# HG changeset patch # User Kim Alvefur # Date 1510231355 -3600 # Node ID d48d4d9ccae7cde678be24898918d31a6b307f0a # Parent 1ffbd73c54ba28d8294f918b440a20d0d6e5eaae mod_storage_xmlarchive: Add a prosodyctl command for migrating to/from the internal storage format diff -r 1ffbd73c54ba -r d48d4d9ccae7 mod_storage_xmlarchive/README.markdown --- a/mod_storage_xmlarchive/README.markdown Thu Nov 09 13:41:36 2017 +0100 +++ b/mod_storage_xmlarchive/README.markdown Thu Nov 09 13:42:35 2017 +0100 @@ -45,3 +45,21 @@ 0.9 Should work 0.8 Does not work ------ --------------- + +Conversion to or from internal storage +-------------------------------------- + +This module stores data in a way that overlaps with the more recent +archive support in `mod_storage_internal`, meaning eg [mod\_migrate] +will not be able to cleanly convert to or from the `xmlarchive` format. + +To mitigate this, an migration command has been added to +`mod_storage_xmlarchive`: + +``` bash +prosodyctl mod_storage_xmlarchive convert $DIR internal $STORE $JID +``` + +Where `$DIR` is `to` or `from`, `$STORE` is eg `archive` or `archive2` +for MAM and `muc_log` for MUC logs. Finally, `$JID` is the JID of the +user or MUC room to me migrated, which can be repeated. diff -r 1ffbd73c54ba -r d48d4d9ccae7 mod_storage_xmlarchive/mod_storage_xmlarchive.lua --- a/mod_storage_xmlarchive/mod_storage_xmlarchive.lua Thu Nov 09 13:41:36 2017 +0100 +++ b/mod_storage_xmlarchive/mod_storage_xmlarchive.lua Thu Nov 09 13:42:35 2017 +0100 @@ -301,3 +301,92 @@ end module:provides("storage", provider); + + +function module.command(arg) + local jid = require "util.jid"; + if arg[1] == "convert" and (arg[2] == "to" or arg[2] == "from") and arg[4] then + local convert; + if arg[2] == "to" then + local xml = require "util.xml"; + function convert(user, host, store) + local dates, err = archive.dates({ host = host, store = store }, user); + if not dates then assert(not err, err); return end + assert(dm.list_store(user, host, store, nil)); + for _, date in ipairs(dates) do + print(date); + local items = assert(dm.list_load(user .. "@" .. date, host, store)); + local xmlfile = assert(io.open(dm.getpath(user .. "@" .. date, host, store, "xml"))); + for _, item in ipairs(items) do + assert(xmlfile:seek("set", item.offset)); + local data = assert(xmlfile:read(item.length)); + assert(#data == item.length, "short read"); + data = assert(xml.parse(data)); + data = st.preserialize(data); + data.key = item.id; + data.with = item.with; + data.when = tonumber(item.when) or dt.parse(item.when); + data.attr.stamp = item.when; + data.attr.stamp_legacy = dt.legacy(data.when); + assert(dm.list_append(user, host, store, data)); + end + end + end + else -- convert from internal + function convert(user, host, store) + local items, err = dm.list_load(user, host, store); + if not items then assert(not err, err); return end + local dates = {}; + local dayitems, date, xmlfile; + for _, item in ipairs(items) do + local meta = { + id = item.key; + with = item.with; + when = item.when or dt.parse(item.attr.stamp); + }; + local current_date = dt.date(meta.when); + if current_date ~= date then + if xmlfile then + assert(xmlfile:close()); + end + if dayitems then + assert(dm.list_store(user .. "@" .. date, host, store, dayitems)); + end + print(current_date); + dayitems = {}; + date = current_date; + table.insert(dates, date); + xmlfile = assert(io.open(dm.getpath(user .. "@" .. date, host, store, "xml"), "w")); + end + item.attr.stamp, item.attr.stamp_legacy = nil, nil; + local stanza = tostring(st.deserialize(item)) .. "\n"; + meta.offset, meta.length = xmlfile:seek(), #stanza; + assert(xmlfile:write(stanza)); + table.insert(dayitems, meta); + end + assert(xmlfile:close()); + assert(dm.list_store(user .. "@" .. date, host, store, dayitems)); + assert(dm.list_store(user, host, store, dates)); + end + end + + local store = arg[4]; + if arg[3] == "internal" then + for i = 5, #arg do + local user, host = jid.prepped_split(arg[i]); + if not user then + print(string.format("Argument #%d (%q) is an invalid JID, aborting", i, arg[i])); + os.exit(1); + end + convert(user, host, store); + end + print("Done"); + return 0; + else + print("Currently only conversion to/from mod_storage_internal is supported"); + print("Check out https://modules.prosody.im/mod_migrate"); + end + end + print("prosodyctl mod_storage_xmlarchive convert (from|to) internal (archive|archive2|muc_log) user@host"); +end +