Software /
code /
prosody-modules
Changeset
6032:a9fe4a50f935
mod_bookmarks2: remove merged module
author | Menel <menel@snikket.de> |
---|---|
date | Thu, 31 Oct 2024 13:53:48 +0100 |
parents | 6031:2c6b14207271 |
children | 6033:8cb37a497e4c |
files | mod_bookmarks2/README.md mod_bookmarks2/mod_bookmarks2.lua mod_bookmarks2/tests/bookmarks2.scs mod_bookmarks2/tests/conversion.scs |
diffstat | 4 files changed, 3 insertions(+), 894 deletions(-) [+] |
line wrap: on
line diff
--- a/mod_bookmarks2/README.md Thu Oct 31 13:48:22 2024 +0100 +++ b/mod_bookmarks2/README.md Thu Oct 31 13:53:48 2024 +0100 @@ -1,37 +1,10 @@ --- labels: - 'Stage-Merged' -summary: Synchronise bookmarks between Private XML, legacy PEP, and PEP +summary: Synchronise bookmarks between Private XML, legacy PEP, and pep, XEP-0048 and XEP-0402 ... ::: {.alert .alert-info} -**Deprecatation notice:** This module has been merged into Prosody as -[mod_bookmarks][doc:modules:mod_bookmarks]. Users of Prosody **0.12** -and later should switch to that. +This module has been merged into Prosody since version 0.12, +see [mod_bookmarks][doc:modules:mod_bookmarks]. ::: - -Introduction ------------- - -This module fetches users’ bookmarks from Private XML (or legacy PEP) and -pushes them to PEP on login, and then redirects any Private XML query (or -legacy PEP) to PEP. This allows interoperability between older clients that -use [XEP-0048](https://xmpp.org/extensions/xep-0048.html) and recent clients -which use [XEP-0402](https://xmpp.org/extensions/xep-0402.html). - -Configuration -------------- - -Simply [enable it like most other -modules](https://prosody.im/doc/installing_modules#prosody-modules), no -further configuration is needed. - -Compatibility -------------- - - ------- ----------------------------------------- - 0.12 [Use the official mod_bookmarks module instead][doc:modules:mod_bookmarks] - 0.11 Works - 0.10 Does not work - 0.9 Does not work - ------- -----------------------------------------
--- a/mod_bookmarks2/mod_bookmarks2.lua Thu Oct 31 13:48:22 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,429 +0,0 @@ - -local st = require "util.stanza"; -local jid_split = require "util.jid".split; - -local mod_pep = module:depends "pep"; -local private_storage = module:open_store("private", "map"); - -local namespace = "urn:xmpp:bookmarks:1"; -local namespace_private = "jabber:iq:private"; -local namespace_legacy = "storage:bookmarks"; - -local default_options = { - ["persist_items"] = true; - ["max_items"] = "max"; - ["send_last_published_item"] = "never"; - ["access_model"] = "whitelist"; -}; - -if not pcall(mod_pep.check_node_config, nil, nil, default_options) then - -- 0.11 or earlier not supporting max_items="max" trows an error here - module:log("debug", "Setting max_items=pep_max_items because 'max' is not supported in this version"); - default_options["max_items"] = module:get_option_number("pep_max_items", 256); - default_options["send_last_published_item"] = nil; -- not available in 0.11 -end - -module:hook("account-disco-info", function (event) - -- This Time it’s Serious! - event.reply:tag("feature", { var = namespace.."#compat" }):up(); - event.reply:tag("feature", { var = namespace.."#compat-pep" }):up(); -end); - --- This must be declared on the domain JID, not the account JID. Note that --- this isn’t defined in the XEP. -module:add_feature(namespace_private); - -local function generate_legacy_storage(items) - local storage = st.stanza("storage", { xmlns = namespace_legacy }); - for _, item_id in ipairs(items) do - local item = items[item_id]; - local bookmark = item:get_child("conference", namespace); - local conference = st.stanza("conference", { - jid = item.attr.id, - name = bookmark.attr.name, - autojoin = bookmark.attr.autojoin, - }); - local nick = bookmark:get_child_text("nick"); - if nick ~= nil then - conference:text_tag("nick", nick):up(); - end - local password = bookmark:get_child_text("password"); - if password ~= nil then - conference:text_tag("password", password):up(); - end - storage:add_child(conference); - end - - return storage; -end - -local function on_retrieve_legacy_pep(event) - local stanza, session = event.stanza, event.origin; - local pubsub = stanza:get_child("pubsub", "http://jabber.org/protocol/pubsub"); - if pubsub == nil then - return; - end - - local items = pubsub:get_child("items"); - if items == nil then - return; - end - - local node = items.attr.node; - if node ~= namespace_legacy then - return; - end - - local username = session.username; - local jid = username.."@"..session.host; - local service = mod_pep.get_pep_service(username); - local ok, ret = service:get_items(namespace, session.full_jid); - if not ok then - module:log("error", "Failed to retrieve PEP bookmarks of %s: %s", jid, ret); - session.send(st.error_reply(stanza, "cancel", ret, "Failed to retrive bookmarks from PEP")); - return true; - end - - local storage = generate_legacy_storage(ret); - - module:log("debug", "Sending back legacy PEP for %s: %s", jid, storage); - session.send(st.reply(stanza) - :tag("pubsub", {xmlns = "http://jabber.org/protocol/pubsub"}) - :tag("items", {node = namespace_legacy}) - :tag("item", {id = "current"}) - :add_child(storage)); - return true; -end - -local function on_retrieve_private_xml(event) - local stanza, session = event.stanza, event.origin; - local query = stanza:get_child("query", namespace_private); - if query == nil then - return; - end - - local bookmarks = query:get_child("storage", namespace_legacy); - if bookmarks == nil then - return; - end - - module:log("debug", "Getting private bookmarks: %s", bookmarks); - - local username = session.username; - local jid = username.."@"..session.host; - local service = mod_pep.get_pep_service(username); - local ok, ret = service:get_items(namespace, session.full_jid); - if not ok then - if ret == "item-not-found" then - module:log("debug", "Got no PEP bookmarks item for %s, returning empty private bookmarks", jid); - session.send(st.reply(stanza):add_child(query)); - else - module:log("error", "Failed to retrieve PEP bookmarks of %s: %s", jid, ret); - session.send(st.error_reply(stanza, "cancel", ret, "Failed to retrive bookmarks from PEP")); - end - return true; - end - - local storage = generate_legacy_storage(ret); - - module:log("debug", "Sending back private for %s: %s", jid, storage); - session.send(st.reply(stanza):query(namespace_private):add_child(storage)); - return true; -end - -local function compare_bookmark2(a, b) - if a == nil or b == nil then - return false; - end - local a_conference = a:get_child("conference", namespace); - local b_conference = b:get_child("conference", namespace); - local a_nick = a_conference:get_child_text("nick"); - local b_nick = b_conference:get_child_text("nick"); - local a_password = a_conference:get_child_text("password"); - local b_password = b_conference:get_child_text("password"); - return (a.attr.id == b.attr.id and - a_conference.attr.name == b_conference.attr.name and - a_conference.attr.autojoin == b_conference.attr.autojoin and - a_nick == b_nick and - a_password == b_password); -end - -local function publish_to_pep(jid, bookmarks, synchronise) - local service = mod_pep.get_pep_service(jid_split(jid)); - - if #bookmarks.tags == 0 then - if synchronise then - -- If we set zero legacy bookmarks, purge the bookmarks 2 node. - module:log("debug", "No bookmark in the set, purging instead."); - local ok, err = service:purge(namespace, jid, true); - if not ok and err == "item-not-found" then - -- Nothing there already, all is well. - return true; - end - return ok, err; - else - return true; - end - end - - -- Retrieve the current bookmarks2. - module:log("debug", "Retrieving the current bookmarks 2."); - local has_bookmarks2, ret = service:get_items(namespace, jid); - local bookmarks2; - if not has_bookmarks2 and ret == "item-not-found" then - module:log("debug", "Got item-not-found, assuming it was empty until now, creating."); - local ok, err = service:create(namespace, jid, default_options); - if not ok then - module:log("error", "Creating bookmarks 2 node failed: %s", err); - return ok, err; - end - bookmarks2 = {}; - elseif not has_bookmarks2 then - module:log("debug", "Got %s error, aborting.", ret); - return false, ret; - else - module:log("debug", "Got existing bookmarks2."); - bookmarks2 = ret; - - local ok, err = service:get_node_config(namespace, jid); - if not ok then - module:log("error", "Retrieving bookmarks 2 node config failed: %s", err); - return ok, err; - end - - local options = err; - for key, value in pairs(default_options) do - if options[key] and options[key] ~= value then - module:log("warn", "Overriding bookmarks 2 configuration for %s, from %s to %s", jid, options[key], value); - options[key] = value; - end - end - - local ok, err = service:set_node_config(namespace, jid, options); - if not ok then - module:log("error", "Setting bookmarks 2 node config failed: %s", err); - return ok, err; - end - end - - -- Get a list of all items we may want to remove. - local to_remove = {}; - for i in ipairs(bookmarks2) do - to_remove[bookmarks2[i]] = true; - end - - for bookmark in bookmarks:childtags("conference", namespace_legacy) do - -- Create the new conference element by copying everything from the legacy one. - local conference = st.stanza("conference", { - xmlns = namespace, - name = bookmark.attr.name, - autojoin = bookmark.attr.autojoin, - }); - local nick = bookmark:get_child_text("nick"); - if nick ~= nil then - conference:text_tag("nick", nick):up(); - end - local password = bookmark:get_child_text("password"); - if password ~= nil then - conference:text_tag("password", password):up(); - end - - -- Create its wrapper. - local item = st.stanza("item", { xmlns = "http://jabber.org/protocol/pubsub", id = bookmark.attr.jid }) - :add_child(conference); - - -- Then publish it only if it’s a new one or updating a previous one. - if compare_bookmark2(item, bookmarks2[bookmark.attr.jid]) then - module:log("debug", "Item %s identical to the previous one, skipping.", item.attr.id); - to_remove[bookmark.attr.jid] = nil; - else - if bookmarks2[bookmark.attr.jid] == nil then - module:log("debug", "Item %s not existing previously, publishing.", item.attr.id); - else - module:log("debug", "Item %s different from the previous one, publishing.", item.attr.id); - to_remove[bookmark.attr.jid] = nil; - end - local ok, err = service:publish(namespace, jid, bookmark.attr.jid, item, default_options); - if not ok then - module:log("error", "Publishing item %s failed: %s", item.attr.id, err); - return ok, err; - end - end - end - - -- Now handle retracting items that have been removed. - if synchronise then - for id in pairs(to_remove) do - module:log("debug", "Item %s removed from bookmarks.", id); - local ok, err = service:retract(namespace, jid, id, st.stanza("retract", { id = id })); - if not ok then - module:log("error", "Retracting item %s failed: %s", id, err); - return ok, err; - end - end - end - return true; -end - --- Synchronise legacy PEP to PEP. -local function on_publish_legacy_pep(event) - local stanza, session = event.stanza, event.origin; - local pubsub = stanza:get_child("pubsub", "http://jabber.org/protocol/pubsub"); - if pubsub == nil then - return; - end - - local publish = pubsub:get_child("publish"); - if publish == nil or publish.attr.node ~= namespace_legacy then - return; - end - - local item = publish:get_child("item"); - if item == nil then - return; - end - - -- Here we ignore the item id, it’ll be generated as 'current' anyway. - - local bookmarks = item:get_child("storage", namespace_legacy); - if bookmarks == nil then - return; - end - - -- We also ignore the publish-options. - - module:log("debug", "Legacy PEP bookmarks set by client, publishing to PEP."); - - local ok, err = publish_to_pep(session.full_jid, bookmarks, true); - if not ok then - module:log("error", "Failed to publish to PEP bookmarks for %s@%s: %s", session.username, session.host, err); - session.send(st.error_reply(stanza, "cancel", "internal-server-error", "Failed to store bookmarks to PEP")); - return true; - end - - session.send(st.reply(stanza)); - return true; -end - --- Synchronise Private XML to PEP. -local function on_publish_private_xml(event) - local stanza, session = event.stanza, event.origin; - local query = stanza:get_child("query", namespace_private); - if query == nil then - return; - end - - local bookmarks = query:get_child("storage", namespace_legacy); - if bookmarks == nil then - return; - end - - module:log("debug", "Private bookmarks set by client, publishing to PEP."); - - local ok, err = publish_to_pep(session.full_jid, bookmarks, true); - if not ok then - module:log("error", "Failed to publish to PEP bookmarks for %s@%s: %s", session.username, session.host, err); - session.send(st.error_reply(stanza, "cancel", "internal-server-error", "Failed to store bookmarks to PEP")); - return true; - end - - session.send(st.reply(stanza)); - return true; -end - -local function migrate_legacy_bookmarks(event) - local session = event.session; - local username = session.username; - local service = mod_pep.get_pep_service(username); - local jid = username.."@"..session.host; - - local ok, ret = service:get_items(namespace_legacy, session.full_jid); - if ok then - module:log("debug", "Legacy PEP bookmarks found for %s, migrating.", jid); - local failed = false; - for _, item_id in ipairs(ret) do - local item = ret[item_id]; - if item.attr.id ~= "current" then - module:log("warn", "Legacy PEP bookmarks for %s isn’t using 'current' as its id: %s", jid, item.attr.id); - end - local bookmarks = item:get_child("storage", namespace_legacy); - module:log("debug", "Got legacy PEP bookmarks of %s: %s", jid, bookmarks); - - local ok, err = publish_to_pep(session.full_jid, bookmarks, false); - if not ok then - module:log("error", "Failed to store legacy PEP bookmarks to bookmarks 2 for %s, aborting migration: %s", jid, err); - failed = true; - break; - end - end - if not failed then - module:log("debug", "Successfully migrated legacy PEP bookmarks of %s to bookmarks 2, attempting deletion of the node.", jid); - local ok, err = service:delete(namespace_legacy, jid); - if not ok then - module:log("error", "Failed to delete legacy PEP bookmarks for %s: %s", jid, err); - end - end - end - - local data, err = private_storage:get(username, "storage:storage:bookmarks"); - if not data then - module:log("debug", "No existing legacy bookmarks for %s, migration already done: %s", jid, err); - local ok, ret2 = service:get_items(namespace, session.full_jid); - if not ok or not ret2 then - module:log("debug", "Additionally, no bookmarks 2 were existing for %s, assuming empty.", jid); - module:fire_event("bookmarks/empty", { session = session }); - end - return; - end - local bookmarks = st.deserialize(data); - module:log("debug", "Got legacy bookmarks of %s: %s", jid, bookmarks); - - module:log("debug", "Going to store legacy bookmarks to bookmarks 2 %s.", jid); - local ok, err = publish_to_pep(session.full_jid, bookmarks, false); - if not ok then - module:log("error", "Failed to store legacy bookmarks to bookmarks 2 for %s, aborting migration: %s", jid, err); - return; - end - module:log("debug", "Stored legacy bookmarks to bookmarks 2 for %s.", jid); - - local ok, err = private_storage:set(username, "storage:storage:bookmarks", nil); - if not ok then - module:log("error", "Failed to remove legacy bookmarks of %s: %s", jid, err); - return; - end - module:log("debug", "Removed legacy bookmarks of %s, migration done!", jid); -end - -local function on_node_created(event) - local service, node, actor = event.service, event.node, event.actor; - if node ~= namespace_legacy then - return; - end - - module:log("debug", "Something tried to create legacy PEP bookmarks for %s.", actor); - local ok, err = service:delete(namespace_legacy, actor); - if not ok then - module:log("error", "Failed to delete legacy PEP bookmarks for %s: %s", actor, err); - end - module:log("debug", "Legacy PEP bookmarks node of %s deleted.", actor); -end - -module:hook("iq/bare/jabber:iq:private:query", function (event) - if event.stanza.attr.type == "get" then - return on_retrieve_private_xml(event); - else - return on_publish_private_xml(event); - end -end, 1); -module:hook("iq/bare/http://jabber.org/protocol/pubsub:pubsub", function (event) - if event.stanza.attr.type == "get" then - return on_retrieve_legacy_pep(event); - else - return on_publish_legacy_pep(event); - end -end, 1); -module:hook("resource-bind", migrate_legacy_bookmarks); -module:handle_items("pep-service", function (event) - local service = event.item.service; - module:hook_object_event(service.events, "node-created", on_node_created); -end, function () end, true);
--- a/mod_bookmarks2/tests/bookmarks2.scs Thu Oct 31 13:48:22 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -# Pubsub: Bookmarks 2.0 - -[Client] Juliet - jid: admin@localhost - password: password - -// admin@localhost is assumed to have node creation privileges - ---------- - -Juliet connects - --- Generated with https://gitlab.com/xmpp-rs/xmpp-parsers: --- cargo run --example=generate-caps https://code.matthewwild.co.uk/scansion/ <<< "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' name='scansion' type='bot'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='urn:xmpp:bookmarks:1+notify'/></query>" -Juliet sends: - <presence id='presence0'> - <c xmlns='http://jabber.org/protocol/caps' - hash='sha-1' - node='https://code.matthewwild.co.uk/scansion/' - ver='CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - <c xmlns='urn:xmpp:caps'> - <hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>OTy9GPCvBZRvqzOHmD/ThA1WbBH3tNoeKbdqKQCRPHc=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='sha3-256'>f/rxDeTf6HyjQ382V3GEG/UfAs5IeclC05jBSBnVQCI=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='blake2b-256'>ucfqg/NrLj0omE+26hYMrbpcmxHcU4Z3hfAQIF+6tt0=</hash> - </c> - </presence> - -Juliet receives: - <iq from="${Juliet's JID}" id='disco' type='get'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - </iq> - -Juliet sends: - <iq to="${Juliet's JID}" id='disco' type='result'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='> - <identity category='client' name='scansion' type='bot'/> - <feature var='http://jabber.org/protocol/disco#info'/> - <feature var='urn:xmpp:bookmarks:1+notify'/> - </query> - </iq> - -Juliet sends: - <iq type='set' id='pub0'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Play's the Thing' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </publish> - <publish-options> - <x xmlns='jabber:x:data' type='submit'> - <field var='FORM_TYPE' type='hidden'> - <value>http://jabber.org/protocol/pubsub#publish-options</value> - </field> - <field var='pubsub#persist_items'> - <value>true</value> - </field> - <field var='pubsub#max_items'> - <value>255</value> - </field> - <field var='pubsub#send_last_published_item'> - <value>never</value> - </field> - <field var='pubsub#access_model'> - <value>whitelist</value> - </field> - </x> - </publish-options> - </pubsub> - </iq> - -Juliet receives: - <message type='headline' from="${Juliet's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit' publisher="${Juliet's JID}"> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Play's the Thing' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet receives: - <iq type='result' id='pub0'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit'/> - </publish> - </pubsub> - </iq> - -Juliet sends: - <iq type='set' id='pub1'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Orchard' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </publish> - <publish-options> - <x xmlns='jabber:x:data' type='submit'> - <field var='FORM_TYPE' type='hidden'> - <value>http://jabber.org/protocol/pubsub#publish-options</value> - </field> - <field var='pubsub#persist_items'> - <value>true</value> - </field> - <field var='pubsub#max_items'> - <value>255</value> - </field> - <field var='pubsub#send_last_published_item'> - <value>never</value> - </field> - <field var='pubsub#access_model'> - <value>whitelist</value> - </field> - </x> - </publish-options> - </pubsub> - </iq> - -Juliet receives: - <message type='headline' from="${Juliet's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit' publisher="${Juliet's JID}"> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Orchard' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet receives: - <iq type='result' id='pub1'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit'/> - </publish> - </pubsub> - </iq> - -Juliet sends: - <iq type='set' id='retract0'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <retract node='urn:xmpp:bookmarks:1' notify='1'> - <item id='theplay@conference.shakespeare.lit'/> - </retract> - </pubsub> - </iq> - -Juliet receives: - <message type='headline' from="${Juliet's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <retract id='theplay@conference.shakespeare.lit'/> - </items> - </event> - </message> - -Juliet receives: - <iq type='result' id='retract0'/> - -Juliet disconnects - -// vim: syntax=xml:
--- a/mod_bookmarks2/tests/conversion.scs Thu Oct 31 13:48:22 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,254 +0,0 @@ -# Pubsub: Bookmarks 2.0 - -[Client] Juliet-old - jid: admin@localhost - password: password - -[Client] Juliet-new - jid: admin@localhost - password: password - -// admin@localhost is assumed to have node creation privileges - ---------- - -Juliet-new connects - --- Generated with https://gitlab.com/xmpp-rs/xmpp-parsers: --- cargo run --example=generate-caps https://code.matthewwild.co.uk/scansion/ <<< "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' name='scansion' type='bot'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='urn:xmpp:bookmarks:1+notify'/></query>" -Juliet-new sends: - <presence id='presence0'> - <c xmlns='http://jabber.org/protocol/caps' - hash='sha-1' - node='https://code.matthewwild.co.uk/scansion/' - ver='CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - <c xmlns='urn:xmpp:caps'> - <hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>OTy9GPCvBZRvqzOHmD/ThA1WbBH3tNoeKbdqKQCRPHc=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='sha3-256'>f/rxDeTf6HyjQ382V3GEG/UfAs5IeclC05jBSBnVQCI=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='blake2b-256'>ucfqg/NrLj0omE+26hYMrbpcmxHcU4Z3hfAQIF+6tt0=</hash> - </c> - </presence> - -Juliet-new receives: - <iq from="${Juliet-new's JID}" id='disco' type='get'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - </iq> - -Juliet-new sends: - <iq to="${Juliet-new's JID}" id='disco' type='result'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='> - <identity category='client' name='scansion' type='bot'/> - <feature var='http://jabber.org/protocol/disco#info'/> - <feature var='urn:xmpp:bookmarks:1+notify'/> - </query> - </iq> - -Juliet-old connects - -Juliet-old sends: - <iq type='get' id='get0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='pub0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Play's the Thing' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='pub0'/> - -Juliet-old sends: - <iq type='get' id='get1'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get1'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='pub1'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Orchard' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='pub1'/> - -Juliet-old sends: - <iq type='get' id='get2'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get2'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='retract0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <retract id='theplay@conference.shakespeare.lit'/> - </items> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='retract0'/> - -Juliet-old sends: - <iq type='get' id='get3'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get3'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='purge0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <purge node='urn:xmpp:bookmarks:1'/> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='purge0'/> - -Juliet-old sends: - <iq type='get' id='get4'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get4'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old disconnects - -Juliet-new disconnects - -// vim: syntax=xml: