2 local ipairs
, pairs
= ipairs
, pairs
;
3 local setmetatable
= setmetatable
;
4 local tostring = tostring;
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");
21 return parse_xml_real(s
);
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
29 local s
= tostring(xml
);
35 return os_remove(path
);
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
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
});
54 local function removeFromArray(array
, value
)
55 for i
,item
in ipairs(array
) do
62 local function removeStanzaChild(s
, child
)
63 removeFromArray(s
.tags
, child
);
64 removeFromArray(s
, child
);
69 -- In order to support mod_auth_internal_hashed
70 local extended
= "http://prosody.im/protocol/extended-xep0227\1";
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
};
79 for k
, v
in pairs(user
.attr
) do
80 if k
:sub(1, #extended
) == extended
then
81 data
[k
:sub(#extended
+1)] = v
;
87 set
= function(self
, user
, data
)
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
;
96 usere
.attr
[extended
..k
] = v
;
99 return setXml(user
, self
.host
, xml
);
101 return setXml(user
, self
.host
, nil);
106 get
= function(self
, user
)
107 user
= getUserElement(getXml(user
, self
.host
));
109 local vcard
= user
:get_child("vCard", 'vcard-temp');
111 return st
.preserialize(vcard
);
115 set
= function(self
, user
, data
)
116 local xml
= getXml(user
, self
.host
);
117 local usere
= xml
and getUserElement(xml
);
119 local vcard
= usere
:get_child("vCard", 'vcard-temp');
121 removeStanzaChild(usere
, vcard
);
126 vcard
= st
.deserialize(data
);
127 usere
:add_child(vcard
);
129 return setXml(user
, self
.host
, xml
);
135 get
= function(self
, user
)
136 user
= getUserElement(getXml(user
, self
.host
));
138 local private
= user
:get_child("query", "jabber:iq:private");
141 for _
, tag in ipairs(private
.tags
) do
142 r
[tag.name
..":"..tag.attr
.xmlns
] = st
.preserialize(tag);
148 set
= function(self
, user
, data
)
149 local xml
= getXml(user
, self
.host
);
150 local usere
= xml
and getUserElement(xml
);
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
);
168 get
= function(self
, user
)
169 user
= getUserElement(getXml(user
, self
.host
));
171 local roster
= user
:get_child("query", "jabber:iq:roster");
175 version
= roster
.attr
.version
;
179 for item
in roster
:childtags("item") do
182 subscription
= item
.attr
.subscription
,
184 name
= item
.attr
.name
,
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;
198 set
= function(self
, user
, data
)
199 local xml
= getXml(user
, self
.host
);
200 local usere
= xml
and getUserElement(xml
);
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
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
217 subscription
= item
.subscription
,
221 for group
in pairs(item
.groups
) do
222 roster
:tag("group"):text(group
):up();
224 roster
:up(); -- move out from item
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
);
240 -----------------------------
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
252 module
:provides("storage", driver
);