whalibmob 5.14.2 → 5.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/Client.js +63 -58
  2. package/package.json +1 -1
package/lib/Client.js CHANGED
@@ -2449,50 +2449,9 @@ class WhalibmobClient extends EventEmitter {
2449
2449
  : fromStr;
2450
2450
  const recipientPhone = targetJid.split('@')[0].split(':')[0].split('.')[0];
2451
2451
 
2452
- // The retry names the device under one address family, but a contact holds
2453
- // a session under each: a Signal address is (user, device) with the server
2454
- // stripped, and the LID user and the phone user are different strings, so
2455
- // one session is not reachable under the other's name. Purging only the
2456
- // family the receipt happened to arrive in left the other one in place —
2457
- // the resend picked it straight back up and produced the same ciphertext
2458
- // the device had just said it could not read, so the message stayed
2459
- // undecryptable until something else rebuilt the session.
2460
- //
2461
- // This is what made a reply sent after a companion-device exchange stick on
2462
- // "Waiting for this message" on the primary: the receipt came in one family,
2463
- // the stale session sat in the other, and every resend re-used it.
2464
- const recipientUsers = new Set([recipientPhone]);
2465
- const mappedPn = this._lidToPn && this._lidToPn.get(recipientPhone);
2466
- const mappedLid = this._pnToLid && this._pnToLid.get(recipientPhone);
2467
- if (mappedPn) recipientUsers.add(mappedPn);
2468
- if (mappedLid) recipientUsers.add(mappedLid);
2469
-
2470
2452
  _whaDbg('[DBG] RETRY_RECV msgId=' + msgId + ' from=' + fromStr +
2471
- ' users=[' + [...recipientUsers].join(',') + '] — clearing session + resending\n');
2472
-
2473
- // Delete all Signal sessions for the recipient's devices so next send creates fresh pkmsg
2474
- const sigStore = this._signal && this._signal.store;
2475
- if (sigStore && sigStore._sessions) {
2476
- const sessions = sigStore._sessions;
2477
- Object.keys(sessions).forEach(addr => {
2478
- // Match on the user segment rather than a bare prefix: "4071.0" must
2479
- // not be cleared by a retry for "407".
2480
- const user = addr.slice(0, addr.lastIndexOf('.'));
2481
- if (recipientUsers.has(user)) {
2482
- _whaDbg('[DBG] RETRY deleting session for ' + addr);
2483
- delete sessions[addr];
2484
- }
2485
- });
2486
- }
2487
-
2488
- // Clear device manager cache so we re-fetch devices on next send. LID device
2489
- // lists are filed under a "lid:" key, so clearing the bare user only ever
2490
- // dropped the phone-side list.
2491
- if (this._devMgr) {
2492
- const cacheKeys = [];
2493
- for (const u of recipientUsers) cacheKeys.push(u, 'lid:' + u);
2494
- this._devMgr.clearCache(cacheKeys);
2495
- }
2453
+ ' — clearing session + resending\n');
2454
+ this._purgeContactSessions(recipientPhone, 'RETRY');
2496
2455
 
2497
2456
  // Re-send the message — will create fresh pre-key (pkmsg) sessions.
2498
2457
  // A group resend must go back through the group path: it needs a fresh
@@ -2740,6 +2699,59 @@ class WhalibmobClient extends EventEmitter {
2740
2699
  this._retryPending.delete(msgId);
2741
2700
  }
2742
2701
 
2702
+ // Drop every Signal session and cached device list a contact owns, so the
2703
+ // next send to them has to fetch a prekey bundle and open a fresh session.
2704
+ //
2705
+ // A contact holds a session under each address family: a Signal address is
2706
+ // (user, device) with the server stripped, and the LID user and the phone
2707
+ // user are different strings, so one session is not reachable under the
2708
+ // other's name. Clearing only the family whose name happened to be at hand
2709
+ // leaves the other in place, and the next send picks it straight back up —
2710
+ // which is how a message stayed undecryptable on a device that had already
2711
+ // said it could not read it.
2712
+ //
2713
+ // Device lists are filed the same way, LID ones under a "lid:" key, so both
2714
+ // spellings of both users have to go.
2715
+ //
2716
+ // Returns the set of users it resolved, for callers that want to log it.
2717
+ _purgeContactSessions(user, tag) {
2718
+ const label = tag || 'PURGE';
2719
+ const users = new Set([String(user)]);
2720
+ const mappedPn = this._lidToPn && this._lidToPn.get(user);
2721
+ const mappedLid = this._pnToLid && this._pnToLid.get(user);
2722
+ if (mappedPn) users.add(mappedPn);
2723
+ if (mappedLid) users.add(mappedLid);
2724
+
2725
+ const sigStore = this._signal && this._signal.store;
2726
+ if (sigStore && sigStore._sessions) {
2727
+ const sessions = sigStore._sessions;
2728
+ for (const addr of Object.keys(sessions)) {
2729
+ // Match on the address's user segment rather than a bare string
2730
+ // prefix: a purge for "407" must not take "4071.0" with it.
2731
+ const addrUser = addr.slice(0, addr.lastIndexOf('.'));
2732
+ if (users.has(addrUser)) {
2733
+ _whaDbg('[DBG] ' + label + ' deleting session for ' + addr);
2734
+ // Through the store rather than off the object it hands back: a bare
2735
+ // `delete` leaves the file alone and the store unmarked, so nothing
2736
+ // is written on the way out and a restart loads every purged session
2737
+ // straight back — the one case the purge exists to prevent. Not
2738
+ // awaited on purpose; the method's body is synchronous, so the
2739
+ // delete and the save have both happened by the time it returns.
2740
+ sigStore.deleteSession(addr);
2741
+ }
2742
+ }
2743
+ }
2744
+
2745
+ if (this._devMgr) {
2746
+ const cacheKeys = [];
2747
+ for (const u of users) cacheKeys.push(u, 'lid:' + u);
2748
+ this._devMgr.clearCache(cacheKeys);
2749
+ }
2750
+
2751
+ _whaDbg('[DBG] ' + label + ' purged users=[' + [...users].join(',') + ']');
2752
+ return users;
2753
+ }
2754
+
2743
2755
  // Hold a sent message's plaintext so a retry receipt naming it can be
2744
2756
  // answered. Both send paths file their entry here so the bound lives in one
2745
2757
  // place instead of being repeated as a literal at each call site.
@@ -2972,23 +2984,16 @@ class WhalibmobClient extends EventEmitter {
2972
2984
  }
2973
2985
 
2974
2986
  if (type === 'identity') {
2975
- // Server push: a contact re-registered WhatsApp (new identity key / new phone).
2976
- // Their old Signal sessions are no longer valid — clear them and the device
2977
- // cache so the next send builds a fresh pkmsg session from scratch.
2987
+ // Server push: a contact re-registered WhatsApp (new identity key / new
2988
+ // phone). Every session we hold with them is anchored in an identity key
2989
+ // that no longer exists, so all of them have to go — under both the
2990
+ // number and the LID, or the next send reaches for the survivor and the
2991
+ // contact receives ciphertext nothing on their side can open. A retry
2992
+ // receipt cannot rescue that: the session is not stale, it is dead.
2978
2993
  const fromJid = String(attrs.from || '');
2979
2994
  const fromPhone = fromJid.split('@')[0].split(':')[0];
2980
- if (fromPhone && this._devMgr) {
2981
- this._devMgr._dcDel([fromPhone]);
2982
- _whaDbg('[DBG] NOTIF_IDENTITY flushed device cache for re-registered ' + fromPhone);
2983
- }
2984
- if (fromPhone && this._signal && this._signal.store && this._signal.store._sessions) {
2985
- const sessions = this._signal.store._sessions;
2986
- let cleared = 0;
2987
- for (const addr of Object.keys(sessions)) {
2988
- if (addr.startsWith(fromPhone + '.')) { delete sessions[addr]; cleared++; }
2989
- }
2990
- _whaDbg('[DBG] NOTIF_IDENTITY cleared ' + cleared + ' Signal sessions for ' + fromPhone);
2991
- }
2995
+ if (fromPhone) this._purgeContactSessions(fromPhone, 'NOTIF_IDENTITY');
2996
+ this.emit('identity_change', { jid: fromJid, user: fromPhone });
2992
2997
  }
2993
2998
 
2994
2999
  // Ack the notification
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.14.2",
3
+ "version": "5.14.3",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API and web ",
5
5
  "author": "Kunboruto20",
6
6
  "main": "index.js",