aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mod_storage_xep0227.lua
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mod_storage_xep0227.lua')
-rw-r--r--plugins/mod_storage_xep0227.lua178
1 files changed, 178 insertions, 0 deletions
diff --git a/plugins/mod_storage_xep0227.lua b/plugins/mod_storage_xep0227.lua
new file mode 100644
index 00000000..ef227ca3
--- /dev/null
+++ b/plugins/mod_storage_xep0227.lua
@@ -0,0 +1,178 @@
+
+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);