# HG changeset patch # User Emmanuel Gil Peyrot # Date 1640277875 -3600 # Node ID 7ed2467c9bb5927765c0f0e43fc0de523bf09e3e # Parent 15cf32e666da4f7e4a146a6a000f40d758f9f911 mod_bookmarks2: Add simple support for legacy PEP queries This implements the publish and items PubSub queries of XEP-0048 version 1.1, ignoring item id, publish-options, and any other query. diff -r 15cf32e666da -r 7ed2467c9bb5 mod_bookmarks2/README.markdown --- a/mod_bookmarks2/README.markdown Wed Dec 22 15:05:31 2021 +0000 +++ b/mod_bookmarks2/README.markdown Thu Dec 23 17:44:35 2021 +0100 @@ -1,29 +1,17 @@ --- labels: -- 'Stage-Alpha' -summary: Synchronise bookmarks between Private XML and PEP +- 'Stage-Beta' +summary: Synchronise bookmarks between Private XML, legacy PEP, and PEP ... -::: {.alert .alert-warning} -**WARNING:** This module is incompatible with clients which only use -[deprecated PEP bookmarks -(XEP-0048)](https://xmpp.org/extensions/xep-0048.html), such as -[Converse.js](https://conversejs.org). - -If you need to be compatible with these clients, use -[mod\_bookmarks](mod_bookmarks.html) instead. -::: - - Introduction ------------ -This module fetches users’ bookmarks from Private XML and pushes them -to PEP on login, and then redirects any Private XML query to PEP. This -allows interop between older clients that use [XEP-0048: Bookmarks -version 1.0](https://xmpp.org/extensions/attic/xep-0048-1.0.html) and -recent clients which use -[XEP-0402](https://xmpp.org/extensions/xep-0402.html). +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 ------------- diff -r 15cf32e666da -r 7ed2467c9bb5 mod_bookmarks2/mod_bookmarks2.lua --- a/mod_bookmarks2/mod_bookmarks2.lua Wed Dec 22 15:05:31 2021 +0000 +++ b/mod_bookmarks2/mod_bookmarks2.lua Thu Dec 23 17:44:35 2021 +0100 @@ -30,6 +30,72 @@ event.reply:tag("feature", { var = namespace.."#compat" }):up(); end); +local function generate_legacy_storage(items) + local storage = st.stanza("storage", { xmlns = "storage:bookmarks" }); + for _, item_id in ipairs(items) do + local item = items[item_id]; + local conference = st.stanza("conference"); + conference.attr.jid = item.attr.id; + local bookmark = item:get_child("conference", namespace); + conference.attr.name = bookmark.attr.name; + conference.attr.autojoin = bookmark.attr.autojoin; + local nick = bookmark:get_child_text("nick"); + if nick ~= nil then + conference:text_tag("nick", nick, { xmlns = "storage:bookmarks" }):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 ~= "storage:bookmarks" 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 + if ret == "item-not-found" then + module:log("debug", "Got no PEP bookmarks item for %s, returning empty legacy PEP 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 legacy PEP for %s: %s", jid, storage); + session.send(st.reply(stanza) + :tag("pubsub", {xmlns = "http://jabber.org/protocol/pubsub"}) + :tag("items", {node = "storage:bookmarks"}) + :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", "jabber:iq:private"); @@ -59,24 +125,7 @@ return true; end - local storage = st.stanza("storage", { xmlns = "storage:bookmarks" }); - for _, item_id in ipairs(ret) do - local item = ret[item_id]; - local conference = st.stanza("conference"); - conference.attr.jid = item.attr.id; - local bookmark = item:get_child("conference", namespace); - conference.attr.name = bookmark.attr.name; - conference.attr.autojoin = bookmark.attr.autojoin; - local nick = bookmark:get_child_text("nick"); - if nick ~= nil then - conference:text_tag("nick", nick, { xmlns = "storage:bookmarks" }):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 + local storage = generate_legacy_storage(ret); module:log("debug", "Sending back private for %s: %s", jid, storage); session.send(st.reply(stanza):query("jabber:iq:private"):add_child(storage)); @@ -190,6 +239,46 @@ 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 ~= "storage:bookmarks" 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", "storage:bookmarks"); + 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; @@ -203,7 +292,7 @@ return; end - module:log("debug", "Private bookmarks set by client, publishing to pep."); + 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 @@ -300,6 +389,13 @@ 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;