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.
- package/lib/Client.js +63 -58
- 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
|
-
'
|
|
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
|
|
2976
|
-
//
|
|
2977
|
-
//
|
|
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
|
|
2981
|
-
|
|
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
|