mod_csi_simple: Consider messages encrypted payload as important (fixes part of ...
[prosody.git] / plugins / mod_storage_xep0227.lua
blob229ad6b515b26da172d21c10e2427f62618083d8
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;
10 local paths = require"util.paths";
11 local st = require "util.stanza";
12 local parse_xml_real = require "util.xml".parse;
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
67 local handlers = {};
69 -- In order to support mod_auth_internal_hashed
70 local extended = "http://prosody.im/protocol/extended-xep0227\1";
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);
103 end;
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);
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;
125 if data then
126 vcard = st.deserialize(data);
127 usere:add_child(vcard);
129 return setXml(user, self.host, xml);
131 return true;
132 end;
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);
144 return r;
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));
159 usere:add_child(private);
161 return setXml(user, self.host, xml);
163 return true;
164 end;
167 handlers.roster = {
168 get = function(self, user)
169 user = getUserElement(getXml(user, self.host));
170 if user then
171 local roster = user:get_child("query", "jabber:iq:roster");
172 if roster then
173 local r = {
174 [false] = {
175 version = roster.attr.version;
176 pending = {};
179 for item in roster:childtags("item") do
180 r[item.attr.jid] = {
181 jid = item.attr.jid,
182 subscription = item.attr.subscription,
183 ask = item.attr.ask,
184 name = item.attr.name,
185 groups = {};
187 for group in item:childtags("group") do
188 r[item.attr.jid].groups[group:get_text()] = true;
190 for pending in user:childtags("presence", "jabber:client") do
191 r[false].pending[pending.attr.from] = true;
194 return r;
197 end;
198 set = function(self, user, data)
199 local xml = getXml(user, self.host);
200 local usere = xml and getUserElement(xml);
201 if usere then
202 local roster = usere:get_child("query", 'jabber:iq:roster');
203 if roster then removeStanzaChild(usere, roster); end
204 usere:maptags(function (tag)
205 if tag.attr.xmlns == "jabber:client" and tag.name == "presence" and tag.attr.type == "subscribe" then
206 return nil;
208 return tag;
209 end);
210 if data and next(data) ~= nil then
211 roster = st.stanza("query", {xmlns='jabber:iq:roster'});
212 usere:add_child(roster);
213 for jid, item in pairs(data) do
214 if jid then
215 roster:tag("item", {
216 jid = jid,
217 subscription = item.subscription,
218 ask = item.ask,
219 name = item.name,
221 for group in pairs(item.groups) do
222 roster:tag("group"):text(group):up();
224 roster:up(); -- move out from item
225 else
226 roster.attr.version = item.version;
227 for pending_jid in pairs(item.pending) do
228 usere:add_child(st.presence({ from = pending_jid, type = "subscribe" }));
233 return setXml(user, self.host, xml);
235 return true;
236 end;
240 -----------------------------
241 local driver = {};
243 function driver:open(datastore, typ) -- luacheck: ignore 212/self
244 if typ and typ ~= "keyval" then return nil, "unsupported-store"; end
245 local handler = handlers[datastore];
246 if not handler then return nil, "unsupported-datastore"; end
247 local instance = setmetatable({ host = module.host; datastore = datastore; }, { __index = handler });
248 if instance.init then instance:init(); end
249 return instance;
252 module:provides("storage", driver);