mirror of https://github.com/bjc/prosody
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
7.5 KiB
232 lines
7.5 KiB
|
|
local setmetatable = setmetatable;
|
|
local error = error;
|
|
local unpack = unpack;
|
|
local module = module;
|
|
local tostring = tostring;
|
|
local pairs, next = pairs, next;
|
|
local prosody = prosody;
|
|
local assert = assert;
|
|
local require = require;
|
|
local st = require "util.stanza";
|
|
local DBI = require "DBI";
|
|
|
|
-- connect to db
|
|
local params = module:get_option("sql_ejabberd") or error("No sql_ejabberd config option");
|
|
local database;
|
|
do
|
|
module:log("debug", "Opening database: %s", "dbi:"..params.driver..":"..params.database);
|
|
prosody.unlock_globals();
|
|
local dbh, err = DBI.Connect(
|
|
params.driver, params.database,
|
|
params.username, params.password,
|
|
params.host, params.port
|
|
);
|
|
prosody.lock_globals();
|
|
assert(dbh, err);
|
|
dbh:autocommit(true);
|
|
database = dbh;
|
|
end
|
|
|
|
-- initialize db
|
|
local ejabberd_init = module:require("ejabberd_init");
|
|
ejabberd_init.init(database);
|
|
|
|
local sqlcache = {};
|
|
local function prepare(sql)
|
|
module:log("debug", "query: %s", sql);
|
|
local err;
|
|
local r = sqlcache[sql];
|
|
if not r then
|
|
r, err = database:prepare(sql);
|
|
if not r then error("Unable to prepare SQL statement: "..err); end
|
|
sqlcache[sql] = r;
|
|
end
|
|
return r;
|
|
end
|
|
|
|
local _parse_xml = module:require("xmlparse");
|
|
local function parse_xml(str)
|
|
local s = _parse_xml(str);
|
|
if s and not s.gsub then
|
|
return st.preserialize(s);
|
|
end
|
|
end
|
|
local function unparse_xml(s)
|
|
return tostring(st.deserialize(s));
|
|
end
|
|
|
|
|
|
local handlers = {};
|
|
|
|
handlers.accounts = {
|
|
get = function(self, user)
|
|
local select = self:query("select password from users where username=? and host=?", user, self.host);
|
|
local row = select and select:fetch();
|
|
if row then return { password = row[1] }; end
|
|
end;
|
|
set = function(self, user, data)
|
|
if data and data.password then
|
|
return self:modify("update users set password=? where username=? and host=?", data.password, user, self.host)
|
|
or self:modify("insert into users (username, host, password) values (?, ?, ?)", user, self.host, data.password);
|
|
else
|
|
return self:modify("delete from users where username=? and host=?", user, self.host);
|
|
end
|
|
end;
|
|
};
|
|
handlers.vcard = {
|
|
get = function(self, user)
|
|
local select = self:query("select vcard from vcard where username=? and host=?", user, self.host);
|
|
local row = select and select:fetch();
|
|
if row then return parse_xml(row[1]); end
|
|
end;
|
|
set = function(self, user, data)
|
|
if data then
|
|
data = unparse_xml(data);
|
|
return self:modify("update vcard set vcard=? where username=? and host=?", data, user, self.host)
|
|
or self:modify("insert into vcard (username, host, vcard) values (?, ?, ?)", user, self.host, data);
|
|
else
|
|
return self:modify("delete from vcard where username=? and host=?", user, self.host);
|
|
end
|
|
end;
|
|
};
|
|
handlers.private = {
|
|
get = function(self, user)
|
|
local select = self:query("select namespace,data from private_storage where username=? and host=?", user, self.host);
|
|
if select then
|
|
local data = {};
|
|
for row in select:rows() do
|
|
data[row[1]] = parse_xml(row[2]);
|
|
end
|
|
return data;
|
|
end
|
|
end;
|
|
set = function(self, user, data)
|
|
if data then
|
|
self:modify("delete from private_storage where username=? and host=?", user, self.host);
|
|
for namespace,text in pairs(data) do
|
|
self:modify("insert into private_storage (username, host, namespace, data) values (?, ?, ?, ?)", user, self.host, namespace, unparse_xml(text));
|
|
end
|
|
return true;
|
|
else
|
|
return self:modify("delete from private_storage where username=? and host=?", user, self.host);
|
|
end
|
|
end;
|
|
-- TODO map_set, map_get
|
|
};
|
|
local subscription_map = { N = "none", B = "both", F = "from", T = "to" };
|
|
local subscription_map_reverse = { none = "N", both = "B", from = "F", to = "T" };
|
|
handlers.roster = {
|
|
get = function(self, user)
|
|
local select = self:query("select jid,nick,subscription,ask,server,subscribe,type from rosterusers where username=?", user);
|
|
if select then
|
|
local roster = { pending = {} };
|
|
for row in select:rows() do
|
|
local jid,nick,subscription,ask,server,subscribe,typ = unpack(row);
|
|
local item = { groups = {} };
|
|
if nick == "" then nick = nil; end
|
|
item.nick = nick;
|
|
item.subscription = subscription_map[subscription];
|
|
if ask == "N" then ask = nil;
|
|
elseif ask == "O" then ask = "subscribe"
|
|
elseif ask == "I" then roster.pending[jid] = true; ask = nil;
|
|
elseif ask == "B" then roster.pending[jid] = true; ask = "subscribe";
|
|
else module:log("debug", "bad roster_item.ask: %s", ask); ask = nil; end
|
|
item.ask = ask;
|
|
roster[jid] = item;
|
|
end
|
|
|
|
select = self:query("select jid,grp from rostergroups where username=?", user);
|
|
if select then
|
|
for row in select:rows() do
|
|
local jid,grp = unpack(row);
|
|
if roster[jid] then roster[jid].groups[grp] = true; end
|
|
end
|
|
end
|
|
select = self:query("select version from roster_version where username=?", user);
|
|
local row = select and select:fetch();
|
|
if row then
|
|
roster[false] = { version = row[1]; };
|
|
end
|
|
return roster;
|
|
end
|
|
end;
|
|
set = function(self, user, data)
|
|
if data and next(data) ~= nil then
|
|
self:modify("delete from rosterusers where username=?", user);
|
|
self:modify("delete from rostergroups where username=?", user);
|
|
self:modify("delete from roster_version where username=?", user);
|
|
local done = {};
|
|
local pending = data.pending or {};
|
|
for jid,item in pairs(data) do
|
|
if jid and jid ~= "pending" then
|
|
local subscription = subscription_map_reverse[item.subscription];
|
|
local ask;
|
|
if pending[jid] then
|
|
if item.ask then ask = "B"; else ask = "I"; end
|
|
else
|
|
if item.ask then ask = "O"; else ask = "N"; end
|
|
end
|
|
local r = self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?, '', '', '')", user, jid, item.nick or "", subscription, ask);
|
|
if not r then module:log("debug", "--- :( %s", tostring(r)); end
|
|
done[jid] = true;
|
|
for group in pairs(item.groups) do
|
|
self:modify("insert into rostergroups (username,jid,grp) values (?, ?, ?)", user, jid, group);
|
|
end
|
|
end
|
|
end
|
|
for jid in pairs(pending) do
|
|
if not done[jid] then
|
|
self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?. ''. ''. '')", user, jid, "", "N", "I");
|
|
end
|
|
end
|
|
local version = data[false] and data[false].version;
|
|
if version then
|
|
self:modify("insert into roster_version (username,version) values (?, ?)", user, version);
|
|
end
|
|
return true;
|
|
else
|
|
self:modify("delete from rosterusers where username=?", user);
|
|
self:modify("delete from rostergroups where username=?", user);
|
|
self:modify("delete from roster_version where username=?", user);
|
|
end
|
|
end;
|
|
};
|
|
|
|
-----------------------------
|
|
local driver = {};
|
|
driver.__index = driver;
|
|
|
|
function driver:query(sql, ...)
|
|
local stmt,err = prepare(sql);
|
|
if not stmt then
|
|
module:log("error", "Failed to prepare SQL [[%s]], error: %s", sql, err);
|
|
return nil, err;
|
|
end
|
|
local ok, err = stmt:execute(...);
|
|
if not ok then
|
|
module:log("error", "Failed to execute SQL [[%s]], error: %s", sql, err);
|
|
return nil, err;
|
|
end
|
|
return stmt;
|
|
end
|
|
function driver:modify(sql, ...)
|
|
local stmt, err = self:query(sql, ...);
|
|
if stmt and stmt:affected() > 0 then return stmt; end
|
|
return nil, err;
|
|
end
|
|
|
|
function driver:open(datastore, typ)
|
|
local instance = setmetatable({ host = module.host, datastore = datastore }, self);
|
|
local handler = handlers[datastore];
|
|
if not handler then return nil; end
|
|
for key,val in pairs(handler) do
|
|
instance[key] = val;
|
|
end
|
|
if instance.init then instance:init(); end
|
|
return instance;
|
|
end
|
|
|
|
-----------------------------
|
|
|
|
module:add_item("data-driver", driver);
|
|
|