2 local setmetatable = setmetatable;
6 local tostring = tostring;
7 local pairs, next = pairs, next;
8 local prosody = prosody;
10 local require = require;
11 local st = require "util.stanza";
12 local DBI = require "DBI";
15 local option_datastore_params = module:get_option("datastore_params") or error("Missing option: datastore_params");
18 local driver, db = unpack(option_datastore_params);
19 module:log("debug", "Opening database: %s", "dbi:"..driver..":"..db);
20 prosody.unlock_globals();
21 local dbh, err = DBI.Connect(unpack(option_datastore_params));
22 prosody.lock_globals();
29 local ejabberd_init = module:require("ejabberd_init");
30 ejabberd_init.init(database);
33 local function prepare(sql)
34 module:log("debug", "query: %s", sql);
36 local r = sqlcache[sql];
38 r, err = database:prepare(sql);
39 if not r then error("Unable to prepare SQL statement: "..err); end
45 local _parse_xml = module:require("xmlparse");
46 local function parse_xml(str)
47 local s = _parse_xml(str);
48 if s and not s.gsub then
49 return st.preserialize(s);
52 local function unparse_xml(s)
53 return tostring(st.deserialize(s));
60 get = function(self, user)
61 local select = self:query("select password from users where username=? and host=?", user, self.host);
62 local row = select and select:fetch();
63 if row then return { password = row[1] }; end
65 set = function(self, user, data)
66 if data and data.password then
67 return self:modify("update users set password=? where username=? and host=?", data.password, user, self.host)
68 or self:modify("insert into users (username, host, password) values (?, ?, ?)", user, self.host, data.password);
70 return self:modify("delete from users where username=? and host=?", user, self.host);
75 get = function(self, user)
76 local select = self:query("select vcard from vcard where username=? and host=?", user, self.host);
77 local row = select and select:fetch();
78 if row then return parse_xml(row[1]); end
80 set = function(self, user, data)
82 data = unparse_xml(data);
83 return self:modify("update vcard set vcard=? where username=? and host=?", data, user, self.host)
84 or self:modify("insert into vcard (username, host, vcard) values (?, ?, ?)", user, self.host, data);
86 return self:modify("delete from vcard where username=? and host=?", user, self.host);
91 get = function(self, user)
92 local select = self:query("select namespace,data from private_storage where username=? and host=?", user, self.host);
95 for row in select:rows() do
96 data[row[1]] = parse_xml(row[2]);
101 set = function(self, user, data)
103 self:modify("delete from private_storage where username=? and host=?", user, self.host);
104 for namespace,text in pairs(data) do
105 self:modify("insert into private_storage (username, host, namespace, data) values (?, ?, ?, ?)", user, self.host, namespace, unparse_xml(text));
109 return self:modify("delete from private_storage where username=? and host=?", user, self.host);
112 -- TODO map_set, map_get
114 local subscription_map = { N = "none", B = "both", F = "from", T = "to" };
115 local subscription_map_reverse = { none = "N", both = "B", from = "F", to = "T" };
117 get = function(self, user)
118 local select = self:query("select jid,nick,subscription,ask,server,subscribe,type from rosterusers where username=?", user);
120 local roster = { pending = {} };
121 for row in select:rows() do
122 local jid,nick,subscription,ask,server,subscribe,typ = unpack(row);
123 local item = { groups = {} };
124 if nick == "" then nick = nil; end
126 item.subscription = subscription_map[subscription];
127 if ask == "N" then ask = nil;
128 elseif ask == "O" then ask = "subscribe"
129 elseif ask == "I" then roster.pending[jid] = true; ask = nil;
130 elseif ask == "B" then roster.pending[jid] = true; ask = "subscribe";
131 else module:log("debug", "bad roster_item.ask: %s", ask); ask = nil; end
136 select = self:query("select jid,grp from rostergroups where username=?", user);
138 for row in select:rows() do
139 local jid,grp = unpack(row);
140 if roster[jid] then roster[jid].groups[grp] = true; end
143 select = self:query("select version from roster_version where username=?", user);
144 local row = select and select:fetch();
146 roster[false] = { version = row[1]; };
151 set = function(self, user, data)
152 if data and next(data) ~= nil then
153 self:modify("delete from rosterusers where username=?", user);
154 self:modify("delete from rostergroups where username=?", user);
155 self:modify("delete from roster_version where username=?", user);
157 local pending = data.pending or {};
158 for jid,item in pairs(data) do
159 if jid and jid ~= "pending" then
160 local subscription = subscription_map_reverse[item.subscription];
163 if item.ask then ask = "B"; else ask = "I"; end
165 if item.ask then ask = "O"; else ask = "N"; end
167 local r = self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?, '', '', '')", user, jid, item.nick or "", subscription, ask);
168 if not r then module:log("debug", "--- :( %s", tostring(r)); end
170 for group in pairs(item.groups) do
171 self:modify("insert into rostergroups (username,jid,grp) values (?, ?, ?)", user, jid, group);
175 for jid in pairs(pending) do
176 if not done[jid] then
177 self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?. ''. ''. '')", user, jid, "", "N", "I");
180 local version = data[false] and data[false].version;
182 self:modify("insert into roster_version (username,version) values (?, ?)", user, version);
186 self:modify("delete from rosterusers where username=?", user);
187 self:modify("delete from rostergroups where username=?", user);
188 self:modify("delete from roster_version where username=?", user);
193 -----------------------------
195 driver.__index = driver;
197 function driver:query(sql, ...)
198 local stmt,err = prepare(sql);
200 module:log("error", "Failed to prepare SQL [[%s]], error: %s", sql, err);
203 local ok, err = stmt:execute(...);
205 module:log("error", "Failed to execute SQL [[%s]], error: %s", sql, err);
210 function driver:modify(sql, ...)
211 local stmt, err = self:query(sql, ...);
212 if stmt and stmt:affected() > 0 then return stmt; end
216 function driver:open(datastore, typ)
217 local instance = setmetatable({ host = module.host, datastore = datastore }, self);
218 local handler = handlers[datastore];
219 if not handler then return nil; end
220 for key,val in pairs(handler) do
223 if instance.init then instance:init(); end
227 -----------------------------
229 module:add_item("data-driver", driver);