Software /
code /
prosody
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(); |