2
0
Эх сурвалжийг харах

Refactor MUC stanza parsing

- Tighten up queries to search for direct children
- Remove `is_me` in favor of `is_self`
JC Brand 7 сар өмнө
parent
commit
0dba95b83e

+ 1 - 1
src/headless/plugins/muc/muc.js

@@ -1808,7 +1808,7 @@ class MUC extends ModelWithMessages(ColorAwareModel(ChatBoxBase)) {
             resource: Strophe.getResourceFromJid(jid) || occupant?.attributes?.resource,
         };
 
-        if (attrs.is_me) {
+        if (attrs.is_self) {
             let modified = false;
             if (attrs.codes.includes(converse.MUC_NICK_CHANGED_CODE)) {
                 modified = true;

+ 43 - 24
src/headless/plugins/muc/parsers.js

@@ -27,7 +27,7 @@ import {
     isValidReceiptRequest,
     throwErrorIfInvalidForward,
 } from '../../shared/parsers';
-import { ACTION_INFO_CODES, NEW_NICK_CODES, STATUS_CODE_STANZAS } from './constants.js';
+import { STATUS_CODE_STANZAS } from './constants.js';
 
 const { Strophe, sizzle, u } = converse.env;
 const { NS } = Strophe;
@@ -71,7 +71,7 @@ export function getMEPActivities (stanza) {
  * @returns {Object}
  */
 function getJIDFromMUCUserData (stanza) {
-    const item = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop();
+    const item = sizzle(`message > x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop();
     return item?.getAttribute('jid');
 }
 
@@ -124,7 +124,7 @@ function getStatusCodes(stanza, type) {
     /**
      * @typedef {import('./types').MUCStatusCode} MUCStatusCode
      */
-    const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza)
+    const codes = sizzle(`${type} > x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza)
         .map(/** @param {Element} s */ (s) => s.getAttribute('code'))
         .filter(
             /** @param {MUCStatusCode} c */
@@ -175,42 +175,62 @@ function getSender (attrs, chatbox) {
 
 /**
  * Parses a passed in message stanza and returns an object of attributes.
- * @param {Element} stanza - The message stanza
+ * @param {Element} original_stanza - The message stanza
  * @param {MUC} chatbox
  * @returns {Promise<MUCMessageAttributes|StanzaParseError>}
  */
-export async function parseMUCMessage (stanza, chatbox) {
-    throwErrorIfInvalidForward(stanza);
+export async function parseMUCMessage (original_stanza, chatbox) {
+    throwErrorIfInvalidForward(original_stanza);
 
-    const selector = `[xmlns="${NS.MAM}"] > forwarded[xmlns="${NS.FORWARD}"] > message`;
-    const original_stanza = stanza;
-    stanza = sizzle(selector, stanza).pop() || stanza;
+    const forwarded_stanza = sizzle(
+        `result[xmlns="${NS.MAM}"] > forwarded[xmlns="${NS.FORWARD}"] > message`,
+        original_stanza
+    ).pop();
 
+    const stanza = forwarded_stanza || original_stanza;
     if (sizzle(`message > forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).length) {
         return new StanzaParseError(
             stanza,
             `Invalid Stanza: Forged MAM groupchat message from ${stanza.getAttribute('from')}`,
         );
     }
-    const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop();
+
+    let delay;
+    let body;
+
+    if (forwarded_stanza) {
+        if (sizzle(`message > forwarded[xmlns="${Strophe.NS.FORWARD}"]`, forwarded_stanza).length) {
+            return new StanzaParseError(
+                original_stanza,
+                `Invalid Stanza: Forged MAM groupchat message from ${original_stanza.getAttribute('from')}`,
+            );
+        }
+        delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, forwarded_stanza.parentElement).pop();
+        body = forwarded_stanza.querySelector(':scope > body')?.textContent?.trim();
+    } else {
+        delay = sizzle(`message > delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop();
+        body = original_stanza.querySelector(':scope > body')?.textContent?.trim();
+    }
+
+
     const from = stanza.getAttribute('from');
     const marker = getChatMarker(stanza);
 
     let attrs = /** @type {MUCMessageAttributes} */(Object.assign(
         {
             from,
+            body,
             'activities': getMEPActivities(stanza),
-            'body': stanza.querySelector(':scope > body')?.textContent?.trim(),
             'chat_state': getChatState(stanza),
             'from_muc': Strophe.getBareJidFromJid(from),
             'is_archived': isArchived(original_stanza),
             'is_carbon': isCarbon(original_stanza),
             'is_delayed': !!delay,
-            'is_forwarded': !!stanza.querySelector('forwarded'),
+            'is_forwarded': !!sizzle(`message > forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).length,
             'is_headline': isHeadline(stanza),
-            'is_markable': !!sizzle(`markable[xmlns="${Strophe.NS.MARKERS}"]`, stanza).length,
+            'is_markable': !!sizzle(`message > markable[xmlns="${Strophe.NS.MARKERS}"]`, stanza).length,
             'is_marker': !!marker,
-            'is_unstyled': !!sizzle(`unstyled[xmlns="${Strophe.NS.STYLING}"]`, stanza).length,
+            'is_unstyled': !!sizzle(`message > unstyled[xmlns="${Strophe.NS.STYLING}"]`, stanza).length,
             'marker_id': marker && marker.getAttribute('id'),
             'msgid': stanza.getAttribute('id') || original_stanza.getAttribute('id'),
             'nick': Strophe.unescapeNode(Strophe.getResourceFromJid(from)),
@@ -218,8 +238,8 @@ export async function parseMUCMessage (stanza, chatbox) {
             'receipt_id': getReceiptId(stanza),
             'received': new Date().toISOString(),
             'references': getReferences(stanza),
-            'subject': stanza.querySelector('subject')?.textContent,
-            'thread': stanza.querySelector('thread')?.textContent,
+            'subject': stanza.querySelector(':scope > subject')?.textContent,
+            'thread': stanza.querySelector(':scope > thread')?.textContent,
             'time': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : new Date().toISOString(),
             'to': stanza.getAttribute('to'),
             'type': stanza.getAttribute('type')
@@ -247,16 +267,16 @@ export async function parseMUCMessage (stanza, chatbox) {
 
     if (attrs.is_archived && original_stanza.getAttribute('from') !== attrs.from_muc) {
         return new StanzaParseError(
-            stanza,
+            original_stanza,
             `Invalid Stanza: Forged MAM message from ${original_stanza.getAttribute('from')}`,
         );
     } else if (attrs.is_archived && original_stanza.getAttribute('from') !== chatbox.get('jid')) {
         return new StanzaParseError(
-            stanza,
+            original_stanza,
             `Invalid Stanza: Forged MAM groupchat message from ${stanza.getAttribute('from')}`,
         );
     } else if (attrs.is_carbon) {
-        return new StanzaParseError(stanza, 'Invalid Stanza: MUC messages SHOULD NOT be XEP-0280 carbon copied');
+        return new StanzaParseError(original_stanza, 'Invalid Stanza: MUC messages SHOULD NOT be XEP-0280 carbon copied');
     }
 
     // We prefer to use one of the XEP-0359 unique and stable stanza IDs as the Model id, to avoid duplicates.
@@ -266,7 +286,7 @@ export async function parseMUCMessage (stanza, chatbox) {
      * *Hook* which allows plugins to add additional parsing
      * @event _converse#parseMUCMessage
      */
-    attrs = await api.hook('parseMUCMessage', stanza, attrs);
+    attrs = await api.hook('parseMUCMessage', original_stanza, attrs);
 
     // We call this after the hook, to allow plugins to decrypt encrypted
     // messages, since we need to parse the message text to determine whether
@@ -318,7 +338,7 @@ function parsePresenceUserItem(stanza, nick) {
      * @typedef {import('./types').MUCAffiliation} MUCAffiliation
      * @typedef {import('./types').MUCRole} MUCRole
      */
-    const item = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop();
+    const item = sizzle(`presence > x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop();
     if (item) {
         const actor = item.querySelector('actor');
         return {
@@ -354,14 +374,13 @@ export function parseMUCPresence (stanza, chatbox) {
     const nick = Strophe.getResourceFromJid(from);
     const attrs = /** @type {MUCPresenceAttributes} */({
         from,
-        is_me: !!stanza.querySelector("status[code='110']"),
         nick,
         occupant_id: getOccupantID(stanza, chatbox),
         type,
         status: stanza.querySelector(':scope > status')?.textContent ?? undefined,
         show: stanza.querySelector(':scope > show')?.textContent ?? (type !== 'unavailable' ? 'online' : 'offline'),
-        image_hash: sizzle(`:scope > x[xmlns="${Strophe.NS.VCARDUPDATE}"] photo`, stanza).pop()?.textContent,
-        hats: sizzle(`:scope x[xmlns="${Strophe.NS.MUC_HATS}"] hat`, stanza).map(/** @param {Element} h */(h) => ({
+        image_hash: sizzle(`presence > x[xmlns="${Strophe.NS.VCARDUPDATE}"] photo`, stanza).pop()?.textContent,
+        hats: sizzle(`presence > hats[xmlns="${Strophe.NS.MUC_HATS}"] hat`, stanza).map(/** @param {Element} h */(h) => ({
             title: h.getAttribute('title'),
             uri: h.getAttribute('uri')
         })),

+ 0 - 1
src/headless/plugins/muc/types.ts

@@ -105,7 +105,6 @@ export type MUCPresenceAttributes = MUCPresenceItemAttributes & {
     from: string; // The sender JID (${muc_jid}/${nick})
     hats: Array<MUCHat>; // An array of XEP-0317 hats
     image_hash?: string;
-    is_me?: boolean;
     is_self: boolean;
     nick: string; // The nickname of the sender
     occupant_id: string; // The XEP-0421 occupant ID

+ 2 - 2
src/headless/types/plugins/muc/parsers.d.ts

@@ -6,11 +6,11 @@
 export function getMEPActivities(stanza: Element): any[];
 /**
  * Parses a passed in message stanza and returns an object of attributes.
- * @param {Element} stanza - The message stanza
+ * @param {Element} original_stanza - The message stanza
  * @param {MUC} chatbox
  * @returns {Promise<MUCMessageAttributes|StanzaParseError>}
  */
-export function parseMUCMessage(stanza: Element, chatbox: MUC): Promise<MUCMessageAttributes | StanzaParseError>;
+export function parseMUCMessage(original_stanza: Element, chatbox: MUC): Promise<MUCMessageAttributes | StanzaParseError>;
 /**
  * Given an IQ stanza with a member list, create an array of objects containing
  * known member data (e.g. jid, nick, role, affiliation).

+ 0 - 1
src/headless/types/plugins/muc/types.d.ts

@@ -73,7 +73,6 @@ export type MUCPresenceAttributes = MUCPresenceItemAttributes & {
     from: string;
     hats: Array<MUCHat>;
     image_hash?: string;
-    is_me?: boolean;
     is_self: boolean;
     nick: string;
     occupant_id: string;