Eliminate direct setfenv usage
[prosody.git] / plugins / storage / ejabberdstore.lib.lua
1 \r
2 local handlers = {};\r
3 \r
4 handlers.accounts = {\r
5         get = function(self, user)\r
6                 local select = self:query("select password from users where username=?", user);\r
7                 local row = select and select:fetch();\r
8                 if row then return { password = row[1] }; end\r
9         end;\r
10         set = function(self, user, data)\r
11                 if data and data.password then\r
12                         return self:modify("update users set password=? where username=?", data.password, user)\r
13                                 or self:modify("insert into users (username, password) values (?, ?)", user, data.password);\r
14                 else\r
15                         return self:modify("delete from users where username=?", user);\r
16                 end\r
17         end;\r
18 };\r
19 handlers.vcard = {\r
20         get = function(self, user)\r
21                 local select = self:query("select vcard from vcard where username=?", user);\r
22                 local row = select and select:fetch();\r
23                 if row then return parse_xml(row[1]); end\r
24         end;\r
25         set = function(self, user, data)\r
26                 if data then\r
27                         data = unparse_xml(data);\r
28                         return self:modify("update vcard set vcard=? where username=?", data, user)\r
29                                 or self:modify("insert into vcard (username, vcard) values (?, ?)", user, data);\r
30                 else\r
31                         return self:modify("delete from vcard where username=?", user);\r
32                 end\r
33         end;\r
34 };\r
35 handlers.private = {\r
36         get = function(self, user)\r
37                 local select = self:query("select namespace,data from private_storage where username=?", user);\r
38                 if select then\r
39                         local data = {};\r
40                         for row in select:rows() do\r
41                                 data[row[1]] = parse_xml(row[2]);\r
42                         end\r
43                         return data;\r
44                 end\r
45         end;\r
46         set = function(self, user, data)\r
47                 if data then\r
48                         self:modify("delete from private_storage where username=?", user);\r
49                         for namespace,text in pairs(data) do\r
50                                 self:modify("insert into private_storage (username, namespace, data) values (?, ?, ?)", user, namespace, unparse_xml(text));\r
51                         end\r
52                         return true;\r
53                 else\r
54                         return self:modify("delete from private_storage where username=?", user);\r
55                 end\r
56         end;\r
57         -- TODO map_set, map_get\r
58 };\r
59 local subscription_map = { N = "none", B = "both", F = "from", T = "to" };\r
60 local subscription_map_reverse = { none = "N", both = "B", from = "F", to = "T" };\r
61 handlers.roster = {\r
62         get = function(self, user)\r
63                 local select = self:query("select jid,nick,subscription,ask,server,subscribe,type from rosterusers where username=?", user);\r
64                 if select then\r
65                         local roster = { pending = {} };\r
66                         for row in select:rows() do\r
67                                 local jid,nick,subscription,ask,server,subscribe,typ = unpack(row);\r
68                                 local item = { groups = {} };\r
69                                 if nick == "" then nick = nil; end\r
70                                 item.nick = nick;\r
71                                 item.subscription = subscription_map[subscription];\r
72                                 if ask == "N" then ask = nil;\r
73                                 elseif ask == "O" then ask = "subscribe"\r
74                                 elseif ask == "I" then roster.pending[jid] = true; ask = nil;\r
75                                 elseif ask == "B" then roster.pending[jid] = true; ask = "subscribe";\r
76                                 else module:log("debug", "bad roster_item.ask: %s", ask); ask = nil; end\r
77                                 item.ask = ask;\r
78                                 roster[jid] = item;\r
79                         end\r
80                         \r
81                         select = self:query("select jid,grp from rostergroups where username=?", user);\r
82                         if select then\r
83                                 for row in select:rows() do\r
84                                         local jid,grp = unpack(rows);\r
85                                         if roster[jid] then roster[jid].groups[grp] = true; end\r
86                                 end\r
87                         end\r
88                         select = self:query("select version from roster_version where username=?", user);\r
89                         local row = select and select:fetch();\r
90                         if row then\r
91                                 roster[false] = { version = row[1]; };\r
92                         end\r
93                         return roster;\r
94                 end\r
95         end;\r
96         set = function(self, user, data)\r
97                 if data and next(data) ~= nil then\r
98                         self:modify("delete from rosterusers where username=?", user);\r
99                         self:modify("delete from rostergroups where username=?", user);\r
100                         self:modify("delete from roster_version where username=?", user);\r
101                         local done = {};\r
102                         local pending = data.pending or {};\r
103                         for jid,item in pairs(data) do\r
104                                 if jid and jid ~= "pending" then\r
105                                         local subscription = subscription_map_reverse[item.subscription];\r
106                                         local ask;\r
107                                         if pending[jid] then\r
108                                                 if item.ask then ask = "B"; else ask = "I"; end\r
109                                         else\r
110                                                 if item.ask then ask = "O"; else ask = "N"; end\r
111                                         end\r
112                                         local r = self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?, '', '', '')", user, jid, item.nick or "", subscription, ask);\r
113                                         if not r then module:log("debug", "--- :( %s", tostring(r)); end\r
114                                         done[jid] = true;\r
115                                         for group in pairs(item.groups) do\r
116                                                 self:modify("insert into rostergroups (username,jid,grp) values (?, ?, ?)", user, jid, group);\r
117                                         end\r
118                                 end\r
119                         end\r
120                         for jid in pairs(pending) do\r
121                                 if not done[jid] then\r
122                                         self:modify("insert into rosterusers (username,jid,nick,subscription,ask,askmessage,server,subscribe) values (?, ?, ?, ?, ?. ''. ''. '')", user, jid, "", "N", "I");\r
123                                 end\r
124                         end\r
125                         local version = data[false] and data[false].version;\r
126                         if version then\r
127                                 self:modify("insert into roster_version (username,version) values (?, ?)", user, version);\r
128                         end\r
129                         return true;\r
130                 else\r
131                         self:modify("delete from rosterusers where username=?", user);\r
132                         self:modify("delete from rostergroups where username=?", user);\r
133                         self:modify("delete from roster_version where username=?", user);\r
134                 end\r
135         end;\r
136 };\r
137 \r
138 -----------------------------\r
139 local driver = {};\r
140 driver.__index = driver;\r
141 \r
142 function driver:prepare(sql)\r
143         module:log("debug", "query: %s", sql);\r
144         local err;\r
145         if not self.sqlcache then self.sqlcache = {}; end\r
146         local r = self.sqlcache[sql];\r
147         if r then return r; end\r
148         r, err = self.database:prepare(sql);\r
149         if not r then error("Unable to prepare SQL statement: "..err); end\r
150         self.sqlcache[sql] = r;\r
151         return r;\r
152 end\r
153 \r
154 function driver:query(sql, ...)\r
155         local stmt = self:prepare(sql);\r
156         if stmt:execute(...) then return stmt; end\r
157 end\r
158 function driver:modify(sql, ...)\r
159         local stmt = self:query(sql, ...);\r
160         if stmt and stmt:affected() > 0 then return stmt; end\r
161 end\r
162 \r
163 function driver:open(host, datastore, typ)\r
164         local cache_key = host.." "..datastore;\r
165         if self.ds_cache[cache_key] then return self.ds_cache[cache_key]; end\r
166         local instance = setmetatable({}, self);\r
167         instance.host = host;\r
168         instance.datastore = datastore;\r
169         local handler = handlers[datastore];\r
170         if not handler then return nil; end\r
171         for key,val in pairs(handler) do\r
172                 instance[key] = val;\r
173         end\r
174         if instance.init then instance:init(); end\r
175         self.ds_cache[cache_key] = instance;\r
176         return instance;\r
177 end\r
178 \r
179 -----------------------------\r
180 local _M = {};\r
181 \r
182 function _M.new(dbtype, dbname, ...)\r
183         local instance = setmetatable({}, driver);\r
184         instance.__index = instance;\r
185         instance.database = get_database(dbtype, dbname, ...);\r
186         instance.ds_cache = {};\r
187         return instance;\r
188 end\r
189 \r
190 return _M;\r