From 7c6e9c44ef9a3f88f5d1a584d3f3bb62d59fa728 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 22 Nov 2023 15:45:40 -0500 Subject: [PATCH] Inline stream management into sasl2/bind2 --- packages/client/browser.js | 1 + packages/client/index.js | 1 + packages/stream-features/route.js | 4 +- packages/stream-management/index.js | 64 +++++++++++++++++++++++++---- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/packages/client/browser.js b/packages/client/browser.js index adf76d3b9..5ca42e44b 100644 --- a/packages/client/browser.js +++ b/packages/client/browser.js @@ -53,6 +53,7 @@ function client(options = {}) { streamFeatures, entity, middleware, + sasl2, }); const resourceBinding = _resourceBinding( { iqCaller, streamFeatures }, diff --git a/packages/client/index.js b/packages/client/index.js index 5c80566da..f582612e0 100644 --- a/packages/client/index.js +++ b/packages/client/index.js @@ -60,6 +60,7 @@ function client(options = {}) { streamFeatures, entity, middleware, + sasl2, }); const resourceBinding = _resourceBinding( { iqCaller, streamFeatures }, diff --git a/packages/stream-features/route.js b/packages/stream-features/route.js index bd6d64b59..48e3dd3c9 100644 --- a/packages/stream-features/route.js +++ b/packages/stream-features/route.js @@ -6,6 +6,8 @@ module.exports = function route() { return next(); const prevent = await next(); - if (!prevent && entity.jid) entity._status("online", entity.jid); + if (!prevent && entity.jid && entity.status !== "online") { + entity._status("online", entity.jid); + } }; }; diff --git a/packages/stream-management/index.js b/packages/stream-management/index.js index c66dd1407..af416744c 100644 --- a/packages/stream-management/index.js +++ b/packages/stream-management/index.js @@ -44,6 +44,7 @@ module.exports = function streamManagement({ streamFeatures, entity, middleware, + sasl2, }) { let address = null; @@ -88,24 +89,30 @@ module.exports = function streamManagement({ // https://xmpp.org/extensions/xep-0198.html#enable // For client-to-server connections, the client MUST NOT attempt to enable stream management until after it has completed Resource Binding unless it is resuming a previous session + const resumeSuccess = () => { + sm.enabled = true; + if (address) entity.jid = address; + entity.status = "online"; + }; + + const resumeFailed = () => { + sm.id = ""; + sm.enabled = false; + sm.outbound = 0; + }; + streamFeatures.use("sm", NS, async (context, next) => { // Resuming if (sm.id) { try { - await resume(entity, sm.inbound, sm.id); - sm.enabled = true; - entity.jid = address; - entity.status = "online"; + resumeSuccess(await resume(entity, sm.inbound, sm.id)); return true; // If resumption fails, continue with session establishment // eslint-disable-next-line no-unused-vars } catch { - sm.id = ""; - sm.enabled = false; - sm.outbound = 0; + resumeFailed(); } } - // Enabling // Resource binding first @@ -129,5 +136,46 @@ module.exports = function streamManagement({ sm.inbound = 0; }); + sasl2?.inline("sm", NS, async (_, addInline) => { + if (sm.id) { + const success = await addInline( + xml("resume", { xmlns: NS, h: sm.inbound, previd: sm.id }), + ); + const resumed = success.getChild("resumed", NS); + if (resumed) { + resumeSuccess(resumed); + } else { + resumeFailed(); + } + } + }); + + sasl2?.bindInline(NS, async (addInline) => { + const success = await addInline( + xml("enable", { + xmlns: NS, + max: sm.preferredMaximum, + resume: sm.allowResume ? "true" : undefined, + }), + ); + const bound = success.getChild("bound", "urn:xmpp:bind:0"); + if (!bound) return; // Did a resume or something, don't need this + + const enabled = bound?.getChild("enabled", NS); + if (enabled) { + if (sm.outbound_q.length > 0) { + throw "Stream Management assertion failure, queue should be empty after enable"; + } + sm.outbound = 0; + sm.enabled = true; + sm.id = enabled.attrs.id; + sm.max = enabled.attrs.max; + } else { + sm.enabled = false; + } + + sm.inbound = 0; + }); + return sm; };