Comparison

plugins/mod_storage_xep0227.lua @ 12173:270047afa6af

mod_storage_xep0227: Allow overriding the input/output layer for XEP-0227 data This can (and will) be used to support in-memory import/export functions.
author Matthew Wild <mwild1@gmail.com>
date Mon, 10 Jan 2022 15:47:59 +0000
parent 12082:e87563fefd85
child 12174:a38b7cb5fd6a
comparison
equal deleted inserted replaced
12172:2515033c2a74 12173:270047afa6af
18 local st = require "util.stanza"; 18 local st = require "util.stanza";
19 local parse_xml_real = require "util.xml".parse; 19 local parse_xml_real = require "util.xml".parse;
20 20
21 local lfs = require "lfs"; 21 local lfs = require "lfs";
22 22
23 local function getXml(user, host) 23 local function default_get_user_xml(self, user, host)
24 local jid = user.."@"..host; 24 local jid = user.."@"..host;
25 local path = paths.join(prosody.paths.data, jid..".xml"); 25 local path = paths.join(prosody.paths.data, jid..".xml");
26 local f, err = io_open(path); 26 local f, err = io_open(path);
27 if not f then 27 if not f then
28 module:log("debug", "Unable to load XML file for <%s>: %s", jid, err); 28 module:log("debug", "Unable to load XML file for <%s>: %s", jid, err);
31 module:log("debug", "Loaded %s", path); 31 module:log("debug", "Loaded %s", path);
32 local s = f:read("*a"); 32 local s = f:read("*a");
33 f:close(); 33 f:close();
34 return parse_xml_real(s); 34 return parse_xml_real(s);
35 end 35 end
36 local function setXml(user, host, xml) 36 local function default_set_user_xml(user, host, xml)
37 local jid = user.."@"..host; 37 local jid = user.."@"..host;
38 local path = paths.join(prosody.paths.data, jid..".xml"); 38 local path = paths.join(prosody.paths.data, jid..".xml");
39 local f, err = io_open(path, "w"); 39 local f, err = io_open(path, "w");
40 if not f then return f, err; end 40 if not f then return f, err; end
41 if xml then 41 if xml then
82 local scram_hash_name = module:get_option_string("password_hash", "SHA-1"); 82 local scram_hash_name = module:get_option_string("password_hash", "SHA-1");
83 local scram_properties = set.new({ "server_key", "stored_key", "iteration_count", "salt" }); 83 local scram_properties = set.new({ "server_key", "stored_key", "iteration_count", "salt" });
84 84
85 handlers.accounts = { 85 handlers.accounts = {
86 get = function(self, user) 86 get = function(self, user)
87 user = getUserElement(getXml(user, self.host)); 87 user = getUserElement(self:_get_user_xml(user, self.host));
88 local scram_credentials = user and user:get_child_with_attr( 88 local scram_credentials = user and user:get_child_with_attr(
89 "scram-credentials", "urn:xmpp:pie:0#scram", 89 "scram-credentials", "urn:xmpp:pie:0#scram",
90 "mechanism", "SCRAM-"..scram_hash_name 90 "mechanism", "SCRAM-"..scram_hash_name
91 ); 91 );
92 if scram_credentials then 92 if scram_credentials then
108 return data; 108 return data;
109 end 109 end
110 end; 110 end;
111 set = function(self, user, data) 111 set = function(self, user, data)
112 if not data then 112 if not data then
113 return setXml(user, self.host, nil); 113 return self:_set_user_xml(user, self.host, nil);
114 end 114 end
115 115
116 local xml = getXml(user, self.host); 116 local xml = self:_get_user_xml(user, self.host);
117 if not xml then xml = createOuterXml(user, self.host); end 117 if not xml then xml = createOuterXml(user, self.host); end
118 local usere = getUserElement(xml); 118 local usere = getUserElement(xml);
119 119
120 local account_properties = set.new(it.to_array(it.keys(data))); 120 local account_properties = set.new(it.to_array(it.keys(data)));
121 121
139 -- Preserve remaining properties as namespaced attributes 139 -- Preserve remaining properties as namespaced attributes
140 for property in account_properties do 140 for property in account_properties do
141 usere.attr[extended..property] = data[property]; 141 usere.attr[extended..property] = data[property];
142 end 142 end
143 143
144 return setXml(user, self.host, xml); 144 return self:_set_user_xml(user, self.host, xml);
145 end; 145 end;
146 }; 146 };
147 handlers.vcard = { 147 handlers.vcard = {
148 get = function(self, user) 148 get = function(self, user)
149 user = getUserElement(getXml(user, self.host)); 149 user = getUserElement(self:_get_user_xml(user, self.host));
150 if user then 150 if user then
151 local vcard = user:get_child("vCard", 'vcard-temp'); 151 local vcard = user:get_child("vCard", 'vcard-temp');
152 if vcard then 152 if vcard then
153 return st.preserialize(vcard); 153 return st.preserialize(vcard);
154 end 154 end
155 end 155 end
156 end; 156 end;
157 set = function(self, user, data) 157 set = function(self, user, data)
158 local xml = getXml(user, self.host); 158 local xml = self:_get_user_xml(user, self.host);
159 local usere = xml and getUserElement(xml); 159 local usere = xml and getUserElement(xml);
160 if usere then 160 if usere then
161 usere:remove_children("vCard", "vcard-temp"); 161 usere:remove_children("vCard", "vcard-temp");
162 if not data then 162 if not data then
163 -- No data to set, old one deleted, success 163 -- No data to set, old one deleted, success
164 return true; 164 return true;
165 end 165 end
166 local vcard = st.deserialize(data); 166 local vcard = st.deserialize(data);
167 usere:add_child(vcard); 167 usere:add_child(vcard);
168 return setXml(user, self.host, xml); 168 return self:_set_user_xml(user, self.host, xml);
169 end 169 end
170 return true; 170 return true;
171 end; 171 end;
172 }; 172 };
173 handlers.private = { 173 handlers.private = {
174 get = function(self, user) 174 get = function(self, user)
175 user = getUserElement(getXml(user, self.host)); 175 user = getUserElement(self:_get_user_xml(user, self.host));
176 if user then 176 if user then
177 local private = user:get_child("query", "jabber:iq:private"); 177 local private = user:get_child("query", "jabber:iq:private");
178 if private then 178 if private then
179 local r = {}; 179 local r = {};
180 for _, tag in ipairs(private.tags) do 180 for _, tag in ipairs(private.tags) do
183 return r; 183 return r;
184 end 184 end
185 end 185 end
186 end; 186 end;
187 set = function(self, user, data) 187 set = function(self, user, data)
188 local xml = getXml(user, self.host); 188 local xml = self:_get_user_xml(user, self.host);
189 local usere = xml and getUserElement(xml); 189 local usere = xml and getUserElement(xml);
190 if usere then 190 if usere then
191 usere:remove_children("query", "jabber:iq:private"); 191 usere:remove_children("query", "jabber:iq:private");
192 if data and next(data) ~= nil then 192 if data and next(data) ~= nil then
193 local private = st.stanza("query", {xmlns='jabber:iq:private'}); 193 local private = st.stanza("query", {xmlns='jabber:iq:private'});
194 for _,tag in pairs(data) do 194 for _,tag in pairs(data) do
195 private:add_child(st.deserialize(tag)); 195 private:add_child(st.deserialize(tag));
196 end 196 end
197 usere:add_child(private); 197 usere:add_child(private);
198 end 198 end
199 return setXml(user, self.host, xml); 199 return self:_set_user_xml(user, self.host, xml);
200 end 200 end
201 return true; 201 return true;
202 end; 202 end;
203 }; 203 };
204 204
205 handlers.roster = { 205 handlers.roster = {
206 get = function(self, user) 206 get = function(self, user)
207 user = getUserElement(getXml(user, self.host)); 207 user = getUserElement(self:_get_user_xml(user, self.host));
208 if user then 208 if user then
209 local roster = user:get_child("query", "jabber:iq:roster"); 209 local roster = user:get_child("query", "jabber:iq:roster");
210 if roster then 210 if roster then
211 local r = { 211 local r = {
212 [false] = { 212 [false] = {
232 return r; 232 return r;
233 end 233 end
234 end 234 end
235 end; 235 end;
236 set = function(self, user, data) 236 set = function(self, user, data)
237 local xml = getXml(user, self.host); 237 local xml = self:_get_user_xml(user, self.host);
238 local usere = xml and getUserElement(xml); 238 local usere = xml and getUserElement(xml);
239 if usere then 239 if usere then
240 usere:remove_children("query", "jabber:iq:roster"); 240 usere:remove_children("query", "jabber:iq:roster");
241 usere:maptags(function (tag) 241 usere:maptags(function (tag)
242 if tag.attr.xmlns == "jabber:client" and tag.name == "presence" and tag.attr.type == "subscribe" then 242 if tag.attr.xmlns == "jabber:client" and tag.name == "presence" and tag.attr.type == "subscribe" then
265 usere:add_child(st.presence({ from = pending_jid, type = "subscribe" })); 265 usere:add_child(st.presence({ from = pending_jid, type = "subscribe" }));
266 end 266 end
267 end 267 end
268 end 268 end
269 end 269 end
270 return setXml(user, self.host, xml); 270 return self:_set_user_xml(user, self.host, xml);
271 end 271 end
272 return true; 272 return true;
273 end; 273 end;
274 }; 274 };
275 275
276 -- PEP node configuration/etc. (not items) 276 -- PEP node configuration/etc. (not items)
277 local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; 277 local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner";
278 local lib_pubsub = module:require "pubsub"; 278 local lib_pubsub = module:require "pubsub";
279 handlers.pep = { 279 handlers.pep = {
280 get = function (self, user) 280 get = function (self, user)
281 local xml = getXml(user, self.host); 281 local xml = self:_get_user_xml(user, self.host);
282 local user_el = xml and getUserElement(xml); 282 local user_el = xml and getUserElement(xml);
283 if not user_el then 283 if not user_el then
284 return nil; 284 return nil;
285 end 285 end
286 local nodes = { 286 local nodes = {
340 end 340 end
341 end 341 end
342 return nodes; 342 return nodes;
343 end; 343 end;
344 set = function(self, user, data) 344 set = function(self, user, data)
345 local xml = getXml(user, self.host); 345 local xml = self:_get_user_xml(user, self.host);
346 local user_el = xml and getUserElement(xml); 346 local user_el = xml and getUserElement(xml);
347 if not user_el then 347 if not user_el then
348 return true; 348 return true;
349 end 349 end
350 -- Remove existing data, if any 350 -- Remove existing data, if any
378 end 378 end
379 end 379 end
380 380
381 user_el:add_child(owner_el); 381 user_el:add_child(owner_el);
382 382
383 return setXml(user, self.host, xml); 383 return self:_set_user_xml(user, self.host, xml);
384 end; 384 end;
385 }; 385 };
386 386
387 -- PEP items 387 -- PEP items
388 local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; 388 local xmlns_pubsub = "http://jabber.org/protocol/pubsub";
408 return store_names; 408 return store_names;
409 end; 409 end;
410 find = function (self, user, query) 410 find = function (self, user, query)
411 -- query keys: limit, reverse, key (id) 411 -- query keys: limit, reverse, key (id)
412 412
413 local xml = getXml(user, self.host); 413 local xml = self:_get_user_xml(user, self.host);
414 local user_el = xml and getUserElement(xml); 414 local user_el = xml and getUserElement(xml);
415 if not user_el then 415 if not user_el then
416 return nil, "no 227 user element found"; 416 return nil, "no 227 user element found";
417 end 417 end
418 418
463 if v == nil then return nil; end 463 if v == nil then return nil; end
464 return unpack(v, 1, 4); 464 return unpack(v, 1, 4);
465 end; 465 end;
466 end; 466 end;
467 append = function (self, user, key, payload, when, with) --luacheck: ignore 212/when 212/with 212/key 467 append = function (self, user, key, payload, when, with) --luacheck: ignore 212/when 212/with 212/key
468 local xml = getXml(user, self.host); 468 local xml = self:_get_user_xml(user, self.host);
469 local user_el = xml and getUserElement(xml); 469 local user_el = xml and getUserElement(xml);
470 if not user_el then 470 if not user_el then
471 return true; 471 return true;
472 end 472 end
473 473
498 -- Append item to pubsub_el 498 -- Append item to pubsub_el
499 local item_el = st.stanza("item", { id = key }) 499 local item_el = st.stanza("item", { id = key })
500 :add_child(payload); 500 :add_child(payload);
501 node_items_el:add_child(item_el); 501 node_items_el:add_child(item_el);
502 502
503 return setXml(user, self.host, xml); 503 return self:_set_user_xml(user, self.host, xml);
504 end; 504 end;
505 delete = function (self, user, query) 505 delete = function (self, user, query)
506 -- query keys: limit, reverse, key (id) 506 -- query keys: limit, reverse, key (id)
507 507
508 local xml = getXml(user, self.host); 508 local xml = self:_get_user_xml(user, self.host);
509 local user_el = xml and getUserElement(xml); 509 local user_el = xml and getUserElement(xml);
510 if not user_el then 510 if not user_el then
511 return nil, "no 227 user element found"; 511 return nil, "no 227 user element found";
512 end 512 end
513 513
556 if delete_keys:contains(item_el.attr.id) then 556 if delete_keys:contains(item_el.attr.id) then
557 return nil; 557 return nil;
558 end 558 end
559 return item_el; 559 return item_el;
560 end); 560 end);
561 return setXml(user, self.host, xml); 561 return self:_set_user_xml(user, self.host, xml);
562 end; 562 end;
563 }; 563 };
564 564
565 -- MAM archives 565 -- MAM archives
566 local xmlns_pie_mam = "urn:xmpp:pie:0#mam"; 566 local xmlns_pie_mam = "urn:xmpp:pie:0#mam";
567 handlers.archive = { 567 handlers.archive = {
568 find = function (self, user, query) 568 find = function (self, user, query)
569 assert(query == nil, "XEP-0313 queries are not supported on XEP-0227 files"); 569 assert(query == nil, "XEP-0313 queries are not supported on XEP-0227 files");
570 570
571 local xml = getXml(user, self.host); 571 local xml = self:_get_user_xml(user, self.host);
572 local user_el = xml and getUserElement(xml); 572 local user_el = xml and getUserElement(xml);
573 if not user_el then 573 if not user_el then
574 return nil, "no 227 user element found"; 574 return nil, "no 227 user element found";
575 end 575 end
576 576
597 -- id, item, when, with 597 -- id, item, when, with
598 return id, item, when, with; 598 return id, item, when, with;
599 end; 599 end;
600 end; 600 end;
601 append = function (self, user, key, payload, when, with) --luacheck: ignore 212/when 212/with 212/key 601 append = function (self, user, key, payload, when, with) --luacheck: ignore 212/when 212/with 212/key
602 local xml = getXml(user, self.host); 602 local xml = self:_get_user_xml(user, self.host);
603 local user_el = xml and getUserElement(xml); 603 local user_el = xml and getUserElement(xml);
604 if not user_el then 604 if not user_el then
605 return true; 605 return true;
606 end 606 end
607 607
622 :up(); 622 :up();
623 623
624 -- Append item to archive_el 624 -- Append item to archive_el
625 archive_el:add_child(result_el); 625 archive_el:add_child(result_el);
626 626
627 return setXml(user, self.host, xml); 627 return self:_set_user_xml(user, self.host, xml);
628 end; 628 end;
629 }; 629 };
630 630
631 ----------------------------- 631 -----------------------------
632 local driver = {}; 632 local driver = {};
651 local handler = handlers[datastore]; 651 local handler = handlers[datastore];
652 if not handler and datastore:match("^pep_") then 652 if not handler and datastore:match("^pep_") then
653 handler = handlers.pep_; 653 handler = handlers.pep_;
654 end 654 end
655 if not handler then return nil, "unsupported-datastore"; end 655 if not handler then return nil, "unsupported-datastore"; end
656 local instance = setmetatable({ host = module.host; datastore = datastore; users = users; }, { __index = handler }); 656 local instance = setmetatable({
657 host = module.host;
658 datastore = datastore;
659 users = users;
660 _get_user_xml = assert(default_get_user_xml);
661 _set_user_xml = default_set_user_xml;
662 }, {
663 __index = handler;
664 }
665 );
657 if instance.init then instance:init(); end 666 if instance.init then instance:init(); end
667 return instance;
668 end
669
670 -- Custom API that allows some configuration
671 function driver:open_xep0227(datastore, typ, options)
672 local instance, err = self:open(datastore, typ);
673 if not instance then
674 return instance, err;
675 end
676 if options then
677 instance._set_user_xml = assert(options.set_user_xml);
678 instance._get_user_xml = assert(options.get_user_xml);
679 end
658 return instance; 680 return instance;
659 end 681 end
660 682
661 local function get_store_names(self, path) 683 local function get_store_names(self, path)
662 local stores = set.new(); 684 local stores = set.new();