mod_storage_xep0227: Close file handle after reading
[prosody.git] / plugins / mod_storage_xep0227.lua
1
2 local ipairs, pairs = ipairs, pairs;
3 local setmetatable = setmetatable;
4 local tostring = tostring;
5 local next = next;
6 local t_remove = table.remove;
7 local os_remove = os.remove;
8 local io_open = io.open;
9
10 local paths = require"util.paths";
11 local st = require "util.stanza";
12 local parse_xml_real = require "util.xml".parse;
13
14 local function getXml(user, host)
15         local jid = user.."@"..host;
16         local path = paths.join(prosody.paths.data, jid..".xml");
17         local f = io_open(path);
18         if not f then return; end
19         local s = f:read("*a");
20         f:close();
21         return parse_xml_real(s);
22 end
23 local function setXml(user, host, xml)
24         local jid = user.."@"..host;
25         local path = paths.join(prosody.paths.data, jid..".xml");
26         if xml then
27                 local f = io_open(path, "w");
28                 if not f then return; end
29                 local s = tostring(xml);
30                 f:write(s);
31                 f:close();
32                 return true;
33         else
34                 return os_remove(path);
35         end
36 end
37 local function getUserElement(xml)
38         if xml and xml.name == "server-data" then
39                 local host = xml.tags[1];
40                 if host and host.name == "host" then
41                         local user = host.tags[1];
42                         if user and user.name == "user" then
43                                 return user;
44                         end
45                 end
46         end
47 end
48 local function createOuterXml(user, host)
49         return st.stanza("server-data", {xmlns='http://www.xmpp.org/extensions/xep-0227.html#ns'})
50                 :tag("host", {jid=host})
51                         :tag("user", {name = user});
52 end
53 local function removeFromArray(array, value)
54         for i,item in ipairs(array) do
55                 if item == value then
56                         t_remove(array, i);
57                         return;
58                 end
59         end
60 end
61 local function removeStanzaChild(s, child)
62         removeFromArray(s.tags, child);
63         removeFromArray(s, child);
64 end
65
66 local handlers = {};
67
68 handlers.accounts = {
69         get = function(self, user)
70                 local user = getUserElement(getXml(user, self.host));
71                 if user and user.attr.password then
72                         return { password = user.attr.password };
73                 end
74         end;
75         set = function(self, user, data)
76                 if data and data.password then
77                         local xml = getXml(user, self.host);
78                         if not xml then xml = createOuterXml(user, self.host); end
79                         local usere = getUserElement(xml);
80                         usere.attr.password = data.password;
81                         return setXml(user, self.host, xml);
82                 else
83                         return setXml(user, self.host, nil);
84                 end
85         end;
86 };
87 handlers.vcard = {
88         get = function(self, user)
89                 local user = getUserElement(getXml(user, self.host));
90                 if user then
91                         local vcard = user:get_child("vCard", 'vcard-temp');
92                         if vcard then
93                                 return st.preserialize(vcard);
94                         end
95                 end
96         end;
97         set = function(self, user, data)
98                 local xml = getXml(user, self.host);
99                 local usere = xml and getUserElement(xml);
100                 if usere then
101                         local vcard = usere:get_child("vCard", 'vcard-temp');
102                         if vcard then
103                                 removeStanzaChild(usere, vcard);
104                         elseif not data then
105                                 return true;
106                         end
107                         if data then
108                                 vcard = st.deserialize(data);
109                                 usere:add_child(vcard);
110                         end
111                         return setXml(user, self.host, xml);
112                 end
113                 return true;
114         end;
115 };
116 handlers.private = {
117         get = function(self, user)
118                 local user = getUserElement(getXml(user, self.host));
119                 if user then
120                         local private = user:get_child("query", "jabber:iq:private");
121                         if private then
122                                 local r = {};
123                                 for _, tag in ipairs(private.tags) do
124                                         r[tag.name..":"..tag.attr.xmlns] = st.preserialize(tag);
125                                 end
126                                 return r;
127                         end
128                 end
129         end;
130         set = function(self, user, data)
131                 local xml = getXml(user, self.host);
132                 local usere = xml and getUserElement(xml);
133                 if usere then
134                         local private = usere:get_child("query", 'jabber:iq:private');
135                         if private then removeStanzaChild(usere, private); end
136                         if data and next(data) ~= nil then
137                                 private = st.stanza("query", {xmlns='jabber:iq:private'});
138                                 for _,tag in pairs(data) do
139                                         private:add_child(st.deserialize(tag));
140                                 end
141                                 usere:add_child(private);
142                         end
143                         return setXml(user, self.host, xml);
144                 end
145                 return true;
146         end;
147 };
148
149 -----------------------------
150 local driver = {};
151
152 function driver:open(host, datastore, typ)
153         local instance = setmetatable({}, self);
154         instance.host = host;
155         instance.datastore = datastore;
156         local handler = handlers[datastore];
157         if not handler then return nil; end
158         for key,val in pairs(handler) do
159                 instance[key] = val;
160         end
161         if instance.init then instance:init(); end
162         return instance;
163 end
164
165 module:provides("storage", driver);