prosody/plugins/mod_storage_xep0227.lua
2015-05-15 15:32:39 +02:00

178 lines
4.4 KiB
Lua

local ipairs, pairs = ipairs, pairs;
local setmetatable = setmetatable;
local tostring = tostring;
local next = next;
local t_remove = table.remove;
local os_remove = os.remove;
local io_open = io.open;
local paths = require"util.paths";
local st = require "util.stanza";
local parse_xml_real = require "util.xml".parse;
local function getXml(user, host)
local jid = user.."@"..host;
local path = paths.join(prosody.paths.data, jid..".xml");
local f = io_open(path);
if not f then return; end
local s = f:read("*a");
f:close();
return parse_xml_real(s);
end
local function setXml(user, host, xml)
local jid = user.."@"..host;
local path = paths.join(prosody.paths.data, jid..".xml");
local f, err = io_open(path, "w");
if not f then return f, err; end
if xml then
local s = tostring(xml);
f:write(s);
f:close();
return true;
else
f:close();
return os_remove(path);
end
end
local function getUserElement(xml)
if xml and xml.name == "server-data" then
local host = xml.tags[1];
if host and host.name == "host" then
local user = host.tags[1];
if user and user.name == "user" then
return user;
end
end
end
end
local function createOuterXml(user, host)
return st.stanza("server-data", {xmlns='urn:xmpp:pie:0'})
:tag("host", {jid=host})
:tag("user", {name = user});
end
local function removeFromArray(array, value)
for i,item in ipairs(array) do
if item == value then
t_remove(array, i);
return;
end
end
end
local function removeStanzaChild(s, child)
removeFromArray(s.tags, child);
removeFromArray(s, child);
end
local handlers = {};
-- In order to support mod_auth_internal_hashed
local extended = "http://prosody.im/protocol/extended-xep0227\1";
handlers.accounts = {
get = function(self, user)
user = getUserElement(getXml(user, self.host));
if user and user.attr.password then
return { password = user.attr.password };
elseif user then
local data = {};
for k, v in pairs(user.attr) do
if k:sub(1, #extended) == extended then
data[k:sub(#extended+1)] = v;
end
end
return data;
end
end;
set = function(self, user, data)
if data then
local xml = getXml(user, self.host);
if not xml then xml = createOuterXml(user, self.host); end
local usere = getUserElement(xml);
for k, v in pairs(data) do
if k == "password" then
usere.attr.password = v;
else
usere.attr[extended..k] = v;
end
end
return setXml(user, self.host, xml);
else
return setXml(user, self.host, nil);
end
end;
};
handlers.vcard = {
get = function(self, user)
user = getUserElement(getXml(user, self.host));
if user then
local vcard = user:get_child("vCard", 'vcard-temp');
if vcard then
return st.preserialize(vcard);
end
end
end;
set = function(self, user, data)
local xml = getXml(user, self.host);
local usere = xml and getUserElement(xml);
if usere then
local vcard = usere:get_child("vCard", 'vcard-temp');
if vcard then
removeStanzaChild(usere, vcard);
elseif not data then
return true;
end
if data then
vcard = st.deserialize(data);
usere:add_child(vcard);
end
return setXml(user, self.host, xml);
end
return true;
end;
};
handlers.private = {
get = function(self, user)
user = getUserElement(getXml(user, self.host));
if user then
local private = user:get_child("query", "jabber:iq:private");
if private then
local r = {};
for _, tag in ipairs(private.tags) do
r[tag.name..":"..tag.attr.xmlns] = st.preserialize(tag);
end
return r;
end
end
end;
set = function(self, user, data)
local xml = getXml(user, self.host);
local usere = xml and getUserElement(xml);
if usere then
local private = usere:get_child("query", 'jabber:iq:private');
if private then removeStanzaChild(usere, private); end
if data and next(data) ~= nil then
private = st.stanza("query", {xmlns='jabber:iq:private'});
for _,tag in pairs(data) do
private:add_child(st.deserialize(tag));
end
usere:add_child(private);
end
return setXml(user, self.host, xml);
end
return true;
end;
};
-----------------------------
local driver = {};
function driver:open(datastore, typ)
local handler = handlers[datastore];
if not handler then return nil, "unsupported-datastore"; end
local instance = setmetatable({ host = module.host; datastore = datastore; }, { __index = handler });
if instance.init then instance:init(); end
return instance;
end
module:provides("storage", driver);