Merge 0.10->trunk
[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         local f, err = io_open(path, "w");
27         if not f then return f, err; end
28         if xml then
29                 local s = tostring(xml);
30                 f:write(s);
31                 f:close();
32                 return true;
33         else
34                 f:close();
35                 return os_remove(path);
36         end
37 end
38 local function getUserElement(xml)
39         if xml and xml.name == "server-data" then
40                 local host = xml.tags[1];
41                 if host and host.name == "host" then
42                         local user = host.tags[1];
43                         if user and user.name == "user" then
44                                 return user;
45                         end
46                 end
47         end
48 end
49 local function createOuterXml(user, host)
50         return st.stanza("server-data", {xmlns='urn:xmpp:pie:0'})
51                 :tag("host", {jid=host})
52                         :tag("user", {name = user});
53 end
54 local function removeFromArray(array, value)
55         for i,item in ipairs(array) do
56                 if item == value then
57                         t_remove(array, i);
58                         return;
59                 end
60         end
61 end
62 local function removeStanzaChild(s, child)
63         removeFromArray(s.tags, child);
64         removeFromArray(s, child);
65 end
66
67 local handlers = {};
68
69 -- In order to support mod_auth_internal_hashed
70 local extended = "http://prosody.im/protocol/extended-xep0227\1";
71
72 handlers.accounts = {
73         get = function(self, user)
74                 user = getUserElement(getXml(user, self.host));
75                 if user and user.attr.password then
76                         return { password = user.attr.password };
77                 elseif user then
78                         local data = {};
79                         for k, v in pairs(user.attr) do
80                                 if k:sub(1, #extended) == extended then
81                                         data[k:sub(#extended+1)] = v;
82                                 end
83                         end
84                         return data;
85                 end
86         end;
87         set = function(self, user, data)
88                 if data then
89                         local xml = getXml(user, self.host);
90                         if not xml then xml = createOuterXml(user, self.host); end
91                         local usere = getUserElement(xml);
92                         for k, v in pairs(data) do
93                                 if k == "password" then
94                                         usere.attr.password = v;
95                                 else
96                                         usere.attr[extended..k] = v;
97                                 end
98                         end
99                         return setXml(user, self.host, xml);
100                 else
101                         return setXml(user, self.host, nil);
102                 end
103         end;
104 };
105 handlers.vcard = {
106         get = function(self, user)
107                 user = getUserElement(getXml(user, self.host));
108                 if user then
109                         local vcard = user:get_child("vCard", 'vcard-temp');
110                         if vcard then
111                                 return st.preserialize(vcard);
112                         end
113                 end
114         end;
115         set = function(self, user, data)
116                 local xml = getXml(user, self.host);
117                 local usere = xml and getUserElement(xml);
118                 if usere then
119                         local vcard = usere:get_child("vCard", 'vcard-temp');
120                         if vcard then
121                                 removeStanzaChild(usere, vcard);
122                         elseif not data then
123                                 return true;
124                         end
125                         if data then
126                                 vcard = st.deserialize(data);
127                                 usere:add_child(vcard);
128                         end
129                         return setXml(user, self.host, xml);
130                 end
131                 return true;
132         end;
133 };
134 handlers.private = {
135         get = function(self, user)
136                 user = getUserElement(getXml(user, self.host));
137                 if user then
138                         local private = user:get_child("query", "jabber:iq:private");
139                         if private then
140                                 local r = {};
141                                 for _, tag in ipairs(private.tags) do
142                                         r[tag.name..":"..tag.attr.xmlns] = st.preserialize(tag);
143                                 end
144                                 return r;
145                         end
146                 end
147         end;
148         set = function(self, user, data)
149                 local xml = getXml(user, self.host);
150                 local usere = xml and getUserElement(xml);
151                 if usere then
152                         local private = usere:get_child("query", 'jabber:iq:private');
153                         if private then removeStanzaChild(usere, private); end
154                         if data and next(data) ~= nil then
155                                 private = st.stanza("query", {xmlns='jabber:iq:private'});
156                                 for _,tag in pairs(data) do
157                                         private:add_child(st.deserialize(tag));
158                                 end
159                                 usere:add_child(private);
160                         end
161                         return setXml(user, self.host, xml);
162                 end
163                 return true;
164         end;
165 };
166
167 -----------------------------
168 local driver = {};
169
170 function driver:open(datastore, typ)
171         local handler = handlers[datastore];
172         if not handler then return nil, "unsupported-datastore"; end
173         local instance = setmetatable({ host = module.host; datastore = datastore; }, { __index = handler });
174         if instance.init then instance:init(); end
175         return instance;
176 end
177
178 module:provides("storage", driver);