Skip to content

Instantly share code, notes, and snippets.

@bseibold
Last active November 16, 2017 20:54
Show Gist options
  • Save bseibold/5041603 to your computer and use it in GitHub Desktop.
Save bseibold/5041603 to your computer and use it in GitHub Desktop.
Export Prosody data to XEP-0227 / XEP 227 / PIEFXIS format
#!/usr/bin/env lua
--
-- (c) 2013 Bernhard Seibold
-- License: MIT/X11
--
-- Disclaimer:
-- ===========
--
-- This is a quick and *DIRTY* hack to export Prosody data to XEP-0227 format.
-- Use at own risk.
--
-- I'm not a Lua programmer, so please excuse the fugly code.
--
-- Issues:
-- =======
--
-- * Only works with Lua 5.1
-- * Exporting offline message is not supported yet
-- * There are probably lots of xml_escape calls missing and other encoding issues
-- * util.stanza failed when exporting private storage containing an empty table as xmlns.
-- The following patch was used, to be applied at around line 200 in function _dostring:
-- elseif not(k == "xmlns" and v == parentns) then
-- if type(v) == "table" and #v == 0 then
-- t_insert(buf, " "..k.."=''");
-- else
-- t_insert(buf, " "..k.."='"..xml_escape(v).."'");
-- end
-- end
--
-- Usage:
-- ======
--
-- cd /var/lib/prosody
-- lua prosody_to_xep227 yourhost.com >yourhost_com.xml
--
package.path = package.path..";/usr/lib/prosody/?.lua";
package.cpath = package.cpath..";/usr/lib/prosody/?.so";
prosody = { };
prosody.platform = "unknown";
if os.getenv("WINDIR") then
prosody.platform = "windows";
elseif package.config:sub(1,1) == "/" then
prosody.platform = "posix";
end
local lxp = require "lxp";
local st = require "util.stanza";
local xmppstream = require "util.xmppstream";
local new_xmpp_handlers = xmppstream.new_sax_handlers;
local dm = require "util.datamanager"
dm.set_data_path(".");
local ns_separator = xmppstream.ns_separator;
local ns_pattern = xmppstream.ns_pattern;
local format = string.format;
local lfs = require "lfs";
function xml_escape(str)
local escape_table = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" };
return (string.gsub(str, "['&<>\"]", escape_table));
end
function decode(str)
str = string.gsub (str, "+", " ")
str = string.gsub (str, "%%(%x%x)", function(h) return string.char(tonumber(h,16)) end)
str = string.gsub (str, "\r\n", "\n")
return str
end
function encode(s)
return s and (s:gsub("%W", function (c) return format("%%%02x", c:byte()); end));
end
function print_offline(host, user)
local items = {};
local file = format("%s/offline/%s.list", encode(host), user);
if err then return end
local item = function(i) t_insert(items, i); end;
local env = {item};
if not lfs.attributes(file) then return end
x = loadfile(file, nil, {item = function(i) t_insert(items, i); end});
local success, ret = pcall(x);
if not success then
print("ERR loading offline")
-- return
end
print("<offline-messages>");
for _, stanza in ipairs(items) do
stanza = st.deserialize(stanza);
for k,v in pairs(stanza) do
print('K=',k,'V=',v);
end
end
print("</offline-messages>");
end
function print_vcard(host, user)
local file = format("%s/vcard/%s.dat", encode(host), user);
local success, vcard = pcall(loadfile(file));
if not success then
return
end
print(st.deserialize(vcard));
end
function print_privacy(host, user)
local file = format("%s/privacy/%s.dat", encode(host), user);
local success, privacy = pcall(loadfile(file));
if not success then
return
end
print("<query xmlns='jabber:iq:privacy'>");
for k, v in pairs(privacy) do
if k == 'default' then
print(format("<default name='%s'/>", v));
elseif k == 'lists' then
for _, data in pairs(v) do
print(format("<list name='%s'>", xml_escape(data['name'])));
for _,item in pairs(data['items']) do
print("<item");
if item['type'] then print(format("type='%s'", item['type'])) end
if item['action'] then print(format("action='%s'", item['action'])) end
if item['value'] then print(format("value='%s'", item['value'])) end
if item['order'] then print(format("order='%d'", item['order'])) end
print(">");
if item['message'] then print("<message/>") end
if item['presence-in'] then print("<presence-in/>") end
if item['presence-out'] then print("<presence-out/>") end
if item['iq'] then print("<iq/>") end
print("</item>");
end
print("</list>");
end
end
end
print("</query>");
end
function print_private(host, user)
local file = format("%s/private/%s.dat", encode(host), user);
local success, private = pcall(loadfile(file));
if not success then
return
end
print("<query xmlns='jabber:iq:private'>");
for name, data in pairs(private) do
if type(data) == 'table' then
print(st.deserialize(data));
end
end
print("</query>");
end
function print_roster(host, user)
local file = format("%s/roster/%s.dat", encode(host), user);
local success, roster = pcall(loadfile(file));
if not success then
return
end
print("<query xmlns='jabber:iq:roster'>");
for jid, data in pairs(roster) do
if jid == false then
elseif jid == 'pending' then
else
print(format("<item jid='%s' name='%s' subscription='%s'>", jid, xml_escape(data['name'] or ''), data['subscription']));
for group,ignore in pairs(data['groups']) do
print(format("<group>%s</group>", xml_escape(group)));
end
print("</item>");
end
end
print("</query>");
for jid, data in pairs(roster) do
if jid == 'pending' then
for k,v in pairs(data) do
print(format("<presence xmlns='jabber:client' type='subscribe' from='%s'/>", xml_escape(k)));
end
end
end
end
function print_host(host)
print("<?xml version='1.0' encoding='UTF-8'?>");
print("<server-data xmlns='urn:xmpp:pie:0'>");
print(format("<host jid='%s'>", host));
ddir = format("%s/accounts/", encode(host))
for file in lfs.dir(ddir) do
if lfs.attributes(ddir..file, "mode") == "file" then
local base, ext = file:match("^(.*)%.([dalist]+)$");
if ext == 'dat' then
local data, ret = loadfile(ddir..file, {});
local success, ret = pcall(data);
print(format("<user name='%s' password='%s'>", decode(base), ret['password']));
print_roster(host, base);
print_private(host, base);
print_privacy(host, base);
--print_offline(host, base);
print_vcard(host, base);
print("</user>");
end
end
end
print("</host>");
print("</server-data>");
end
print_host(arg[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment