From 985243c7e54442bb2b12dc7cf940446b751568fc Mon Sep 17 00:00:00 2001 From: JC Brand Date: Fri, 17 Jan 2025 16:52:12 +0200 Subject: [PATCH] Use `stx` to create message stanza --- package-lock.json | 6 +- src/headless/package.json | 2 +- src/headless/plugins/chat/message.js | 6 + src/headless/plugins/chat/types.ts | 10 +- src/headless/plugins/muc/occupant.js | 1 + src/headless/plugins/muc/tests/messages.js | 6 +- src/headless/shared/model-with-messages.js | 98 +++++++--------- src/headless/types/plugins/chat/message.d.ts | 7 +- src/headless/types/plugins/chat/model.d.ts | 2 +- src/headless/types/plugins/chat/types.d.ts | 8 +- src/headless/types/plugins/muc/muc.d.ts | 2 +- src/headless/types/plugins/muc/occupant.d.ts | 2 +- src/headless/types/shared/chatbox.d.ts | 2 +- .../types/shared/model-with-messages.d.ts | 6 +- src/plugins/adhoc-views/tests/adhoc.js | 2 + src/plugins/chatview/tests/corrections.js | 22 ++-- src/plugins/chatview/tests/spoilers.js | 105 +++++------------- .../muc-views/tests/muc-private-messages.js | 19 ++-- src/plugins/omemo/utils.js | 62 +++++------ 19 files changed, 165 insertions(+), 203 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3fa29cd84..e34acb192d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9868,8 +9868,8 @@ }, "node_modules/strophe.js": { "version": "3.1.0", - "resolved": "git+ssh://git@github.com/strophe/strophejs.git#b4f3369ba07dee4f04750de6709ea8ebe8adf9a5", - "integrity": "sha512-M/T9Pio3eG7GUzVmQUSNg+XzjFwQ6qhzI+Z3uSwUIItxxpRIB8lQB2Afb0L7lbQiRYB7/9tS03GxksdqjfrS5g==", + "resolved": "git+ssh://git@github.com/strophe/strophejs.git#f54d83199ec62272ee8b4987b2e63f188e1b2e29", + "integrity": "sha512-ivy/25C19VudvLDMPhW4oZ4gIpicc0+AnnBzzV/YUikTbaS/ujy4Y/vO416alCFovqEmcc3AEXoQ4O8KqYEKug==", "license": "MIT", "optionalDependencies": { "@types/jsdom": "^21.1.7", @@ -11121,7 +11121,7 @@ "pluggable.js": "3.0.1", "sizzle": "^2.3.5", "sprintf-js": "^1.1.2", - "strophe.js": "strophe/strophejs#b4f3369ba07dee4f04750de6709ea8ebe8adf9a5", + "strophe.js": "strophe/strophejs#f54d83199ec62272ee8b4987b2e63f188e1b2e29", "urijs": "^1.19.10" }, "devDependencies": {} diff --git a/src/headless/package.json b/src/headless/package.json index 1a5f7de063..1957d9fe10 100644 --- a/src/headless/package.json +++ b/src/headless/package.json @@ -42,7 +42,7 @@ "pluggable.js": "3.0.1", "sizzle": "^2.3.5", "sprintf-js": "^1.1.2", - "strophe.js": "strophe/strophejs#b4f3369ba07dee4f04750de6709ea8ebe8adf9a5", + "strophe.js": "strophe/strophejs#f54d83199ec62272ee8b4987b2e63f188e1b2e29", "urijs": "^1.19.10" }, "devDependencies": {} diff --git a/src/headless/plugins/chat/message.js b/src/headless/plugins/chat/message.js index 556f1fd14f..c3949028c7 100644 --- a/src/headless/plugins/chat/message.js +++ b/src/headless/plugins/chat/message.js @@ -35,6 +35,9 @@ class Message extends ModelWithContact(ColorAwareModel(Model)) { constructor (models, options) { super(models, options); this.file = null; + + /** @type {import('./types').MessageAttributes} */ + this.attributes; } async initialize () { @@ -209,6 +212,9 @@ class Message extends ModelWithContact(ColorAwareModel(Model)) { return api.sendIQ(iq); } + /** + * @param {Element} stanza + */ getUploadRequestMetadata (stanza) { // eslint-disable-line class-methods-use-this const headers = sizzle(`slot[xmlns="${Strophe.NS.HTTPUPLOAD}"] put header`, stanza); // https://xmpp.org/extensions/xep-0363.html#request diff --git a/src/headless/plugins/chat/types.ts b/src/headless/plugins/chat/types.ts index 9ff0613b0b..c96c636be9 100644 --- a/src/headless/plugins/chat/types.ts +++ b/src/headless/plugins/chat/types.ts @@ -1,5 +1,13 @@ import {EncryptionAttrs} from "../../shared/types"; +// Represents a XEP-0372 reference +export type Reference = { + begin: number; + end: number; + type: string; + uri: string; +} + export type MessageErrorAttributes = { is_error: boolean; // Whether an error was received for this message error: string; // The error name @@ -41,7 +49,7 @@ export type MessageAttributes = EncryptionAttrs & MessageErrorAttributes & { plaintext: string; // The decrypted text of this message, in case it was encrypted. receipt_id: string; // The `id` attribute of a XEP-0184 element received: string; // An ISO8601 string recording the time that the message was received - references: Array; // A list of objects representing XEP-0372 references + references: Array; // A list of objects representing XEP-0372 references replace_id: string; // The `id` attribute of a XEP-0308 element retracted: string; // An ISO8601 string recording the time that the message was retracted retracted_id: string; // The `id` attribute of a XEP-424 element diff --git a/src/headless/plugins/muc/occupant.js b/src/headless/plugins/muc/occupant.js index c25a9b19b9..3b3b81f2bd 100644 --- a/src/headless/plugins/muc/occupant.js +++ b/src/headless/plugins/muc/occupant.js @@ -39,6 +39,7 @@ class MUCOccupant extends ModelWithMessages(ColorAwareModel(Model)) { states: [], hidden: true, num_unread: 0, + message_type: 'chat', }; } diff --git a/src/headless/plugins/muc/tests/messages.js b/src/headless/plugins/muc/tests/messages.js index 8edbe2e190..24edd9e72d 100644 --- a/src/headless/plugins/muc/tests/messages.js +++ b/src/headless/plugins/muc/tests/messages.js @@ -128,8 +128,8 @@ describe("A MUC message", function () { const muc_jid = 'lounge@montague.lit'; const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo'); - const received_stanza = u.toStanza(` - + const received_stanza = stx` + @@ -139,7 +139,7 @@ describe("A MUC message", function () { pong - `); + `; await model.handleMessageStanza(received_stanza); await u.waitUntil(() => model.messages.last()); expect(model.messages.last().get('body')).toBe('> ping\n pong'); diff --git a/src/headless/shared/model-with-messages.js b/src/headless/shared/model-with-messages.js index 05cd169558..6201f63818 100644 --- a/src/headless/shared/model-with-messages.js +++ b/src/headless/shared/model-with-messages.js @@ -15,7 +15,7 @@ import { MethodNotImplementedError } from './errors.js'; import { sendMarker, sendReceiptStanza, sendRetractionMessage } from './actions.js'; import { parseMessage } from '../plugins/chat/parsers'; -const { Strophe, $msg, u } = converse.env; +const { Strophe, stx, u } = converse.env; /** * Adds a messages collection to a model and various methods related to sending @@ -155,9 +155,9 @@ export default function ModelWithMessages(BaseModel) { } /** - * @param {MessageAttributes|Error} attrs_or_error + * @param {MessageAttributes|Error} _attrs_or_error */ - async onMessage(attrs_or_error) { + async onMessage(_attrs_or_error) { throw new MethodNotImplementedError('onMessage is not implemented'); } @@ -889,64 +889,48 @@ export default function ModelWithMessages(BaseModel) { /** * Given a {@link Message} return the XML stanza that represents it. * @method ChatBox#createMessageStanza - * @param { Message } message - The message object + * @param {Message} message - The message object */ async createMessageStanza(message) { - const stanza = $msg({ - 'from': message.get('from') || api.connection.get().jid, - 'to': message.get('to') || this.get('jid'), - 'type': this.get('message_type'), - 'id': (message.get('edited') && u.getUniqueId()) || message.get('msgid'), - }) - .c('body') - .t(message.get('body')) - .up() - .c(constants.ACTIVE, { 'xmlns': Strophe.NS.CHATSTATES }) - .root(); - - if (message.get('type') === 'chat') { - stanza.c('request', { 'xmlns': Strophe.NS.RECEIPTS }).root(); - } - - if (!message.get('is_encrypted')) { - if (message.get('is_spoiler')) { - if (message.get('spoiler_hint')) { - stanza.c('spoiler', { 'xmlns': Strophe.NS.SPOILER }, message.get('spoiler_hint')).root(); - } else { - stanza.c('spoiler', { 'xmlns': Strophe.NS.SPOILER }).root(); - } - } - (message.get('references') || []).forEach((reference) => { - const attrs = { - 'xmlns': Strophe.NS.REFERENCE, - 'begin': reference.begin, - 'end': reference.end, - 'type': reference.type, - }; - if (reference.uri) { - attrs.uri = reference.uri; + const { + body, + edited, + is_encrypted, + is_spoiler, + msgid, + oob_url, + origin_id, + references, + spoiler_hint, + type, + } = message.attributes; + + const stanza = stx` + + ${body ? stx`${body}` : ''} + + ${type === 'chat' ? stx`` : ''} + ${!is_encrypted && oob_url ? stx`${oob_url}` : ''} + ${!is_encrypted && is_spoiler ? stx`${spoiler_hint ?? ''}` : ''} + ${ + !is_encrypted + ? references?.map( + (ref) => stx`` + ) + : '' } - stanza.c('reference', attrs).root(); - }); - - if (message.get('oob_url')) { - stanza.c('x', { 'xmlns': Strophe.NS.OUTOFBAND }).c('url').t(message.get('oob_url')).root(); - } - } + ${edited ? stx`` : ''} + ${origin_id ? stx`` : ''} + `; - if (message.get('edited')) { - stanza - .c('replace', { - 'xmlns': Strophe.NS.MESSAGE_CORRECT, - 'id': message.get('msgid'), - }) - .root(); - } - - if (message.get('origin_id')) { - stanza.c('origin-id', { 'xmlns': Strophe.NS.SID, 'id': message.get('origin_id') }).root(); - } - stanza.root(); /** * *Hook* which allows plugins to update an outgoing message stanza * @event _converse#createMessageStanza diff --git a/src/headless/types/plugins/chat/message.d.ts b/src/headless/types/plugins/chat/message.d.ts index 7fa0961b7f..21fed80571 100644 --- a/src/headless/types/plugins/chat/message.d.ts +++ b/src/headless/types/plugins/chat/message.d.ts @@ -157,6 +157,8 @@ declare class Message extends Message_base { is_ephemeral: boolean; }; file: any; + /** @type {import('./types').MessageAttributes} */ + attributes: import("./types").MessageAttributes; initialize(): Promise; chatbox: any; initialized: any; @@ -204,7 +206,10 @@ declare class Message extends Message_base { * @method _converse.Message#sendSlotRequestStanza */ private sendSlotRequestStanza; - getUploadRequestMetadata(stanza: any): { + /** + * @param {Element} stanza + */ + getUploadRequestMetadata(stanza: Element): { headers: { name: string; value: string; diff --git a/src/headless/types/plugins/chat/model.d.ts b/src/headless/types/plugins/chat/model.d.ts index 2fcd6b6737..f96dcd45b1 100644 --- a/src/headless/types/plugins/chat/model.d.ts +++ b/src/headless/types/plugins/chat/model.d.ts @@ -17,7 +17,7 @@ declare const ChatBox_base: { messages: any; fetchMessages(): any; afterMessagesFetched(): void; - onMessage(attrs_or_error: import("./types").MessageAttributes | Error): Promise; + onMessage(_attrs_or_error: import("./types").MessageAttributes | Error): Promise; getUpdatedMessageAttributes(message: import("./message.js").default, attrs: import("./types").MessageAttributes): object; updateMessage(message: import("./message.js").default, attrs: import("./types").MessageAttributes): void; handleCorrection(attrs: import("./types").MessageAttributes | import("../muc/types.js").MUCMessageAttributes): Promise; diff --git a/src/headless/types/plugins/chat/types.d.ts b/src/headless/types/plugins/chat/types.d.ts index 0b1a2adca9..08d7229d09 100644 --- a/src/headless/types/plugins/chat/types.d.ts +++ b/src/headless/types/plugins/chat/types.d.ts @@ -1,4 +1,10 @@ import { EncryptionAttrs } from "../../shared/types"; +export type Reference = { + begin: number; + end: number; + type: string; + uri: string; +}; export type MessageErrorAttributes = { is_error: boolean; error: string; @@ -42,7 +48,7 @@ export type MessageAttributes = EncryptionAttrs & MessageErrorAttributes & { plaintext: string; receipt_id: string; received: string; - references: Array; + references: Array; replace_id: string; retracted: string; retracted_id: string; diff --git a/src/headless/types/plugins/muc/muc.d.ts b/src/headless/types/plugins/muc/muc.d.ts index 21f1359463..bd0e980200 100644 --- a/src/headless/types/plugins/muc/muc.d.ts +++ b/src/headless/types/plugins/muc/muc.d.ts @@ -17,7 +17,7 @@ declare const MUC_base: { messages: any; fetchMessages(): any; afterMessagesFetched(): void; - onMessage(attrs_or_error: import("../chat/types").MessageAttributes | Error): Promise; + onMessage(_attrs_or_error: import("../chat/types").MessageAttributes | Error): Promise; getUpdatedMessageAttributes(message: import("../chat/message.js").default, attrs: import("../chat/types").MessageAttributes): object; updateMessage(message: import("../chat/message.js").default, attrs: import("../chat/types").MessageAttributes): void; handleCorrection(attrs: import("../chat/types").MessageAttributes | import("./types").MUCMessageAttributes): Promise; diff --git a/src/headless/types/plugins/muc/occupant.d.ts b/src/headless/types/plugins/muc/occupant.d.ts index da366175bc..ffa54a5bab 100644 --- a/src/headless/types/plugins/muc/occupant.d.ts +++ b/src/headless/types/plugins/muc/occupant.d.ts @@ -17,7 +17,7 @@ declare const MUCOccupant_base: { messages: any; fetchMessages(): any; afterMessagesFetched(): void; - onMessage(attrs_or_error: import("../chat/types").MessageAttributes | Error): Promise; + onMessage(_attrs_or_error: import("../chat/types").MessageAttributes | Error): Promise; getUpdatedMessageAttributes(message: import("../chat").Message, attrs: import("../chat/types").MessageAttributes): object; updateMessage(message: import("../chat").Message, attrs: import("../chat/types").MessageAttributes): void; handleCorrection(attrs: import("../chat/types").MessageAttributes | import("./types").MUCMessageAttributes): Promise; diff --git a/src/headless/types/shared/chatbox.d.ts b/src/headless/types/shared/chatbox.d.ts index 6466bd79b1..3424913c9a 100644 --- a/src/headless/types/shared/chatbox.d.ts +++ b/src/headless/types/shared/chatbox.d.ts @@ -16,7 +16,7 @@ declare const ChatBoxBase_base: { messages: any; fetchMessages(): any; afterMessagesFetched(): void; - onMessage(attrs_or_error: import("../plugins/chat/types.js").MessageAttributes | Error): Promise; + onMessage(_attrs_or_error: import("../plugins/chat/types.js").MessageAttributes | Error): Promise; getUpdatedMessageAttributes(message: import("../index.js").Message, attrs: import("../plugins/chat/types.js").MessageAttributes): object; updateMessage(message: import("../index.js").Message, attrs: import("../plugins/chat/types.js").MessageAttributes): void; handleCorrection(attrs: import("../plugins/chat/types.js").MessageAttributes | import("../plugins/muc/types.js").MUCMessageAttributes): Promise; diff --git a/src/headless/types/shared/model-with-messages.d.ts b/src/headless/types/shared/model-with-messages.d.ts index ecbc086a46..07087d5486 100644 --- a/src/headless/types/shared/model-with-messages.d.ts +++ b/src/headless/types/shared/model-with-messages.d.ts @@ -38,9 +38,9 @@ export default function ModelWithMessages; + onMessage(_attrs_or_error: import("../plugins/chat/types.ts").MessageAttributes | Error): Promise; /** * @param {Message} message * @param {MessageAttributes} attrs @@ -216,7 +216,7 @@ export default function ModelWithMessages; /** diff --git a/src/plugins/adhoc-views/tests/adhoc.js b/src/plugins/adhoc-views/tests/adhoc.js index 1f96e6fd66..817ed6da53 100644 --- a/src/plugins/adhoc-views/tests/adhoc.js +++ b/src/plugins/adhoc-views/tests/adhoc.js @@ -4,6 +4,8 @@ const { Strophe, sizzle, u, stx } = converse.env; describe("Ad-hoc commands", function () { + beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza })); + it("can be queried for via a modal", mock.initConverse([], {}, async (_converse) => { const { api } = _converse; const entity_jid = 'muc.montague.lit'; diff --git a/src/plugins/chatview/tests/corrections.js b/src/plugins/chatview/tests/corrections.js index fe3e9108cf..89b0131734 100644 --- a/src/plugins/chatview/tests/corrections.js +++ b/src/plugins/chatview/tests/corrections.js @@ -4,6 +4,8 @@ const { Promise, $msg, Strophe, sizzle, u } = converse.env; describe("A Chat Message", function () { + beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza })); + it("can be sent as a correction by using the up arrow", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { @@ -56,16 +58,16 @@ describe("A Chat Message", function () { expect(api.connection.get().send).toHaveBeenCalled(); const msg = api.connection.get().send.calls.all()[0].args[0]; - expect(Strophe.serialize(msg)) - .toBe(``+ - `But soft, what light through yonder window breaks?`+ - ``+ - ``+ - ``+ - ``+ - ``); + expect(msg).toEqualStanza( + stx` + But soft, what light through yonder window breaks? + + + + + `); expect(view.model.messages.models.length).toBe(1); const corrected_message = view.model.messages.at(0); expect(corrected_message.get('msgid')).toBe(first_msg.get('msgid')); diff --git a/src/plugins/chatview/tests/spoilers.js b/src/plugins/chatview/tests/spoilers.js index 4dc3f04154..b966f9bd83 100644 --- a/src/plugins/chatview/tests/spoilers.js +++ b/src/plugins/chatview/tests/spoilers.js @@ -1,5 +1,6 @@ /* global mock, converse */ +const { Strophe, sizzle, $msg, u, stx } = converse.env; const original_timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; const settings = { @@ -11,6 +12,7 @@ const settings = { describe("A spoiler message", function () { + beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza })); beforeEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = 7000)); afterEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = original_timeout)); @@ -20,26 +22,13 @@ describe("A spoiler message", function () { const { api } = _converse; await mock.waitForRoster(_converse, 'current'); const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; - - /* - * And at the end of the story, both of them die! It is so tragic! - * Love story end - * - */ const spoiler_hint = "Love story end" const spoiler = "And at the end of the story, both of them die! It is so tragic!"; - const $msg = converse.env.$msg; - const u = converse.env.utils; - const msg = $msg({ - 'xmlns': 'jabber:client', - 'to': _converse.bare_jid, - 'from': sender_jid, - 'type': 'chat' - }).c('body').t(spoiler).up() - .c('spoiler', { - 'xmlns': 'urn:xmpp:spoiler:0', - }).t(spoiler_hint) - .tree(); + const msg = stx` + + ${spoiler} + ${spoiler_hint} + `; api.connection.get()._dataRecv(mock.createRequest(msg)); await new Promise(resolve => _converse.api.listen.once('chatBoxViewInitialized', resolve)); const view = _converse.chatboxviews.get(sender_jid); @@ -58,23 +47,12 @@ describe("A spoiler message", function () { const { api } = _converse; await mock.waitForRoster(_converse, 'current'); const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; - /* - * And at the end of the story, both of them die! It is so tragic! - * Love story end - * - */ - const $msg = converse.env.$msg; - const u = converse.env.utils; const spoiler = "And at the end of the story, both of them die! It is so tragic!"; - const msg = $msg({ - 'xmlns': 'jabber:client', - 'to': _converse.bare_jid, - 'from': sender_jid, - 'type': 'chat' - }).c('body').t(spoiler).up() - .c('spoiler', { - 'xmlns': 'urn:xmpp:spoiler:0', - }).tree(); + const msg = stx` + + ${spoiler} + + `; api.connection.get()._dataRecv(mock.createRequest(msg)); await new Promise(resolve => _converse.api.listen.once('chatBoxViewInitialized', resolve)); const view = _converse.chatboxviews.get(sender_jid); @@ -97,17 +75,11 @@ describe("A spoiler message", function () { mock.openControlBox(_converse); const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; - const { $pres, Strophe} = converse.env; - const u = converse.env.utils; - // XXX: We need to send a presence from the contact, so that we // have a resource, that resource is then queried to see // whether Strophe.NS.SPOILER is supported, in which case // the spoiler button will appear. - const presence = $pres({ - 'from': contact_jid+'/phone', - 'to': 'romeo@montague.lit' - }); + const presence = stx``; api.connection.get()._dataRecv(mock.createRequest(presence)); await mock.openChatBoxFor(_converse, contact_jid); await mock.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]); @@ -128,21 +100,9 @@ describe("A spoiler message", function () { }); await new Promise(resolve => api.listen.on('sendMessage', resolve)); - /* Test the XML stanza - * - * - * This is the spoiler - * - * - * " - */ const stanza = api.connection.get().send.calls.argsFor(0)[0]; - const spoiler_el = await u.waitUntil(() => stanza.querySelector('spoiler[xmlns="urn:xmpp:spoiler:0"]')); - expect(spoiler_el.textContent).toBe(''); + const spoiler_el = sizzle('spoiler[xmlns="urn:xmpp:spoiler:0"]', stanza).pop(); + expect(spoiler_el.textContent).toBe('') const spoiler = 'This is the spoiler'; const body_el = stanza.querySelector('body'); @@ -174,17 +134,11 @@ describe("A spoiler message", function () { mock.openControlBox(_converse); const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; - const { $pres, Strophe} = converse.env; - const u = converse.env.utils; - // XXX: We need to send a presence from the contact, so that we // have a resource, that resource is then queried to see // whether Strophe.NS.SPOILER is supported, in which case // the spoiler button will appear. - const presence = $pres({ - 'from': contact_jid+'/phone', - 'to': 'romeo@montague.lit' - }); + const presence = stx``; api.connection.get()._dataRecv(mock.createRequest(presence)); await mock.openChatBoxFor(_converse, contact_jid); await mock.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]); @@ -210,21 +164,22 @@ describe("A spoiler message", function () { await new Promise(resolve => api.listen.on('sendMessage', resolve)); const stanza = api.connection.get().send.calls.argsFor(0)[0]; - expect(Strophe.serialize(stanza)).toBe( - ``+ - `This is the spoiler`+ - ``+ - ``+ - `This is the hint`+ - ``+ - `` + expect(stanza).toEqualStanza( + stx` + This is the spoiler + + + This is the hint + + ` ); - await u.waitUntil(() => stanza.querySelector('spoiler[xmlns="urn:xmpp:spoiler:0"]')?.textContent === 'This is the hint'); + const spoiler_el = sizzle('spoiler[xmlns="urn:xmpp:spoiler:0"]', stanza).pop(); + expect(spoiler_el?.textContent).toBe('This is the hint'); const spoiler = 'This is the spoiler' const body_el = stanza.querySelector('body'); diff --git a/src/plugins/muc-views/tests/muc-private-messages.js b/src/plugins/muc-views/tests/muc-private-messages.js index b304ae6681..9557234e39 100644 --- a/src/plugins/muc-views/tests/muc-private-messages.js +++ b/src/plugins/muc-views/tests/muc-private-messages.js @@ -130,15 +130,16 @@ describe('MUC Private Messages', () => { const sent_stanza = api.connection.get().sent_stanzas.pop(); expect(sent_stanza).toEqualStanza(stx` - - hello - - - - `); + + hello + + + + `); }) ); diff --git a/src/plugins/omemo/utils.js b/src/plugins/omemo/utils.js index 7f2f8f8f01..3cd2556941 100644 --- a/src/plugins/omemo/utils.js +++ b/src/plugins/omemo/utils.js @@ -17,7 +17,7 @@ import { MIMETYPES_MAP } from 'utils/file.js'; import { IQError, UserFacingError } from 'shared/errors.js'; import DeviceLists from './devicelists.js'; -const { Strophe, URI, sizzle } = converse.env; +const { Strophe, URI, sizzle, stx } = converse.env; const { CHATROOMS_TYPE, PRIVATE_CHAT_TYPE } = constants; const { appendArrayBuffer, @@ -467,24 +467,6 @@ async function decryptWhisperMessage (attrs) { } } -export function addKeysToMessageStanza (stanza, dicts, iv) { - for (const i in dicts) { - if (Object.prototype.hasOwnProperty.call(dicts, i)) { - const payload = dicts[i].payload; - const device = dicts[i].device; - const prekey = 3 == parseInt(payload.type, 10); - - stanza.c('key', { 'rid': device.get('id') }).t(btoa(payload.body)); - if (prekey) { - stanza.attrs({ 'prekey': prekey }); - } - stanza.up(); - } - } - stanza.c('iv').t(iv).up().up(); - return Promise.resolve(stanza); -} - /** * Given an XML element representing a user's OMEMO bundle, parse it * and return a map. @@ -873,16 +855,6 @@ export async function createOMEMOMessageStanza (chat, data) { throw new Error('No message body to encrypt!'); } const devices = await getBundlesAndBuildSessions(chat); - - // An encrypted header is added to the message for - // each device that is supposed to receive it. - // These headers simply contain the key that the - // payload message is encrypted with, - // and they are separately encrypted using the - // session corresponding to the counterpart device. - stanza.c('encrypted', { 'xmlns': Strophe.NS.OMEMO }) - .c('header', { 'sid': _converse.state.omemo_store.get('device_id') }); - const { key_and_tag, iv, payload } = await omemo.encryptMessage(message.get('plaintext')); // The 16 bytes key and the GCM authentication tag (The tag @@ -892,13 +864,33 @@ export async function createOMEMOMessageStanza (chat, data) { // concatenation is encrypted using the corresponding // long-standing SignalProtocol session. const dicts = await Promise.all(devices - .filter(device => device.get('trusted') != UNTRUSTED && device.get('active')) - .map(device => encryptKey(key_and_tag, device))); + .filter((device) => device.get('trusted') != UNTRUSTED && device.get('active')) + .map((device) => encryptKey(key_and_tag, device))); - stanza = await addKeysToMessageStanza(stanza, dicts, iv); - stanza.c('payload').t(payload).up().up(); - stanza.c('store', { 'xmlns': Strophe.NS.HINTS }).up(); - stanza.c('encryption', { 'xmlns': Strophe.NS.EME, namespace: Strophe.NS.OMEMO }); + // An encrypted header is added to the message for + // each device that is supposed to receive it. + // These headers simply contain the key that the + // payload message is encrypted with, + // and they are separately encrypted using the + // session corresponding to the counterpart device. + stanza.cnode( + stx` +
+ ${dicts.map(({ payload, device }) => { + const prekey = 3 == parseInt(payload.type, 10); + if (prekey) { + return stx`${btoa(payload.body)}`; + } + return stx`${btoa(payload.body)}`; + })} + ${iv} +
+ ${payload} +
` + ).root(); + + stanza.cnode(stx``).root(); + stanza.cnode(stx``).root(); return { message, stanza }; }