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