--- /dev/null
+\r
+local handlers = {};\r
+\r
+handlers.accounts = {\r
+ get = function(self, user)\r
+ local select = self:query("select password from users where username=?", user);\r
+ local row = select and select:fetch();\r
+ if row then return { password = row[1] }; end\r
+ end;\r
+ set = function(self, user, data)\r
+ if data and data.password then\r
+ return self:modify("update users set password=? where username=?", data.password, user)\r
+ or self:modify("insert into users (username, password) values (?, ?)", user, data.password);\r
+ else\r
+ return self:modify("delete from users where username=?", user);\r
+ end\r
+ end;\r
+};\r
+handlers.vcard = {\r
+ get = function(self, user)\r
+ local select = self:query("select vcard from vcard where username=?", user);\r
+ local row = select and select:fetch();\r
+ if row then return parse_xml(row[1]); end\r
+ end;\r
+ set = function(self, user, data)\r
+ if data then\r
+ data = unparse_xml(data);\r
+ return self:modify("update vcard set vcard=? where username=?", data, user)\r
+ or self:modify("insert into vcard (username, vcard) values (?, ?)", user, data);\r
+ else\r
+ return self:modify("delete from vcard where username=?", user);\r
+ end\r
+ end;\r
+};\r
+handlers.private = {\r
+ get = function(self, user)\r
+ local select = self:query("select namespace,data from private_storage where username=?", user);\r
+ if select then\r
+ local data = {};\r
+ for row in select:rows() do\r
+ data[row[1]] = parse_xml(row[2]);\r
+ end\r
+ return data;\r
+ end\r
+ end;\r
+ set = function(self, user, data)\r
+ if data then\r
+ self:modify("delete from private_storage where username=?", user);\r
+ for namespace,text in pairs(data) do\r
+ self:modify("insert into private_storage (username, namespace, data) values (?, ?, ?)", user, namespace, unparse_xml(text));\r
+ end\r
+ return true;\r
+ else\r
+ return self:modify("delete from private_storage where username=?", user);\r
+ end\r
+ end;\r
+ -- TODO map_set, map_get\r
+};\r
+local subscription_map = { N = "none", B = "both", F = "from", T = "to" };\r
+local subscription_map_reverse = { none = "N", both = "B", from = "F", to = "T" };\r
+handlers.roster = {\r
+ get = function(self, user)\r
+ local select = self:query("select jid,nick,subscription,ask,server,subscribe,type from rosterusers where username=?", user);\r
+ if select then\r
+ local roster = { pending = {} };\r
+ for row in select:rows() do\r
+ local jid,nick,subscription,ask,server,subscribe,typ = unpack(row);\r
+ local item = { groups = {} };\r
+ if nick == "" then nick = nil; end\r
+ item.nick = nick;\r
+ item.subscription = subscription_map[subscription];\r
+ if ask == "N" then ask = nil;\r
+ elseif ask == "O" then ask = "subscribe"\r
+ elseif ask == "I" then roster.pending[jid] = true; ask = nil;\r
+ elseif ask == "B" then roster.pending[jid] = true; ask = "subscribe";\r
+ else module:log("debug", "bad roster_item.ask: %s", ask); ask = nil; end\r
+ item.ask = ask;\r
+ roster[jid] = item;\r
+ end\r
+ \r
+ select = self:query("select jid,grp from rostergroups where username=?", user);\r
+ if select then\r
+ for row in select:rows() do\r
+ local jid,grp = unpack(rows);\r
+ if roster[jid] then roster[jid].groups[grp] = true; end\r
+ end\r
+ end\r
+ select = self:query("select version from roster_version where username=?", user);\r
+ local row = select and select:fetch();\r
+ if row then\r
+ roster[false] = { version = row[1]; };\r
+ end\r
+ return roster;\r
+ end\r
+ end;\r
+ set = function(self, user, data)\r
+ if data and next(data) ~= nil then\r
+ self:modify("delete from rosterusers where username=?", user);\r
+ self:modify("delete from rostergroups where username=?", user);\r
+ self:modify("delete from roster_version where username=?", user);\r
+ local done = {};\r
+ local pending = data.pending or {};\r
+ for jid,item in pairs(data) do\r
+ if jid and jid ~= "pending" then\r
+ local subscription = subscription_map_reverse[item.subscription];\r
+ local ask;\r
+ if pending[jid] then\r
+ if item.ask then ask = "B"; else ask = "I"; end\r
+ else\r
+ if item.ask then ask = "O"; else ask = "N"; end\r
+ end\r
+ local r = self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?, '', '', '')", user, jid, item.nick or "", subscription, ask);\r
+ if not r then module:log("debug", "--- :( %s", tostring(r)); end\r
+ done[jid] = true;\r
+ for group in pairs(item.groups) do\r
+ self:modify("insert into rostergroups (username,jid,grp) values (?, ?, ?)", user, jid, group);\r
+ end\r
+ end\r
+ end\r
+ for jid in pairs(pending) do\r
+ if not done[jid] then\r
+ self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?. ''. ''. '')", user, jid, "", "N", "I");\r
+ end\r
+ end\r
+ local version = data[false] and data[false].version;\r
+ if version then\r
+ self:modify("insert into roster_version (username,version) values (?, ?)", user, version);\r
+ end\r
+ return true;\r
+ else\r
+ self:modify("delete from rosterusers where username=?", user);\r
+ self:modify("delete from rostergroups where username=?", user);\r
+ self:modify("delete from roster_version where username=?", user);\r
+ end\r
+ end;\r
+};\r
+\r
+-----------------------------\r
+local driver = {};\r
+driver.__index = driver;\r
+\r
+function driver:prepare(sql)\r
+ module:log("debug", "query: %s", sql);\r
+ local err;\r
+ if not self.sqlcache then self.sqlcache = {}; end\r
+ local r = self.sqlcache[sql];\r
+ if r then return r; end\r
+ r, err = self.database:prepare(sql);\r
+ if not r then error("Unable to prepare SQL statement: "..err); end\r
+ self.sqlcache[sql] = r;\r
+ return r;\r
+end\r
+\r
+function driver:query(sql, ...)\r
+ local stmt = self:prepare(sql);\r
+ if stmt:execute(...) then return stmt; end\r
+end\r
+function driver:modify(sql, ...)\r
+ local stmt = self:query(sql, ...);\r
+ if stmt and stmt:affected() > 0 then return stmt; end\r
+end\r
+\r
+function driver:open(host, datastore, typ)\r
+ local cache_key = host.." "..datastore;\r
+ if self.ds_cache[cache_key] then return self.ds_cache[cache_key]; end\r
+ local instance = setmetatable({}, self);\r
+ instance.host = host;\r
+ instance.datastore = datastore;\r
+ local handler = handlers[datastore];\r
+ if not handler then return nil; end\r
+ for key,val in pairs(handler) do\r
+ instance[key] = val;\r
+ end\r
+ if instance.init then instance:init(); end\r
+ self.ds_cache[cache_key] = instance;\r
+ return instance;\r
+end\r
+\r
+-----------------------------\r
+local _M = {};\r
+\r
+function _M.new(dbtype, dbname, ...)\r
+ local instance = setmetatable({}, driver);\r
+ instance.__index = instance;\r
+ instance.database = get_database(dbtype, dbname, ...);\r
+ instance.ds_cache = {};\r
+ return instance;\r
+end\r
+\r
+return _M;\r
--- /dev/null
+
+module:set_global();
+
+local cache = { data = {} };
+function cache:get(key) return self.data[key]; end
+function cache:set(key, val) self.data[key] = val; return val; end
+
+local DBI = require "DBI";
+function get_database(driver, db, ...)
+ local uri = "dbi:"..driver..":"..db;
+ return cache:get(uri) or cache:set(uri, (function(...)
+ module:log("debug", "Opening database: %s", uri);
+ prosody.unlock_globals();
+ local dbh = assert(DBI.Connect(...));
+ prosody.lock_globals();
+ dbh:autocommit(true)
+ return dbh;
+ end)(driver, db, ...));
+end
+
+local st = require "util.stanza";
+local _parse_xml = module:require("xmlparse");
+parse_xml_real = _parse_xml;
+function parse_xml(str)
+ local s = _parse_xml(str);
+ if s and not s.gsub then
+ return st.preserialize(s);
+ end
+end
+function unparse_xml(s)
+ return tostring(st.deserialize(s));
+end
+
+local drivers = {};
+
+--local driver = module:require("sqlbasic").new("SQLite3", "hello.sqlite");
+local option_datastore = module:get_option("datastore");
+local option_datastore_params = module:get_option("datastore_params") or {};
+if option_datastore then
+ local driver = module:require(option_datastore).new(unpack(option_datastore_params));
+ table.insert(drivers, driver);
+end
+
+local datamanager = require "util.datamanager";
+local olddm = {};
+local dm = {};
+for key,val in pairs(datamanager) do olddm[key] = val; end
+
+do -- driver based on old datamanager
+ local dmd = {};
+ dmd.__index = dmd;
+ function dmd:open(host, datastore)
+ return setmetatable({ host = host, datastore = datastore }, dmd);
+ end
+ function dmd:get(user) return olddm.load(user, self.host, self.datastore); end
+ function dmd:set(user, data) return olddm.store(user, self.host, self.datastore, data); end
+ table.insert(drivers, dmd);
+end
+
+local function open(...)
+ for _,driver in pairs(drivers) do
+ local ds = driver:open(...);
+ if ds then return ds; end
+ end
+end
+
+local _data_path;
+--function dm.set_data_path(path) _data_path = path; end
+--function dm.add_callback(...) end
+--function dm.remove_callback(...) end
+--function dm.getpath(...) end
+function dm.load(username, host, datastore)
+ local x = open(host, datastore);
+ return x:get(username);
+end
+function dm.store(username, host, datastore, data)
+ return open(host, datastore):set(username, data);
+end
+--function dm.list_append(...) return driver:list_append(...); end
+--function dm.list_store(...) return driver:list_store(...); end
+--function dm.list_load(...) return driver:list_load(...); end
+
+for key,val in pairs(dm) do datamanager[key] = val; end