Backups: update to latest integration tests
This commit is contained in:
parent
c7dc4279a1
commit
6f7faf4be8
12 changed files with 239 additions and 88 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -194,7 +194,7 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'signalapp/Signal-Message-Backup-Tests'
|
repository: 'signalapp/Signal-Message-Backup-Tests'
|
||||||
ref: 'fcd73d838997294dc2d27b536cd1112ab33c4ee0'
|
ref: 'a920df75ba02e011f6c56c59c6bb20571162a961'
|
||||||
path: 'backup-integration-tests'
|
path: 'backup-integration-tests'
|
||||||
|
|
||||||
- run: xvfb-run --auto-servernum npm run test-electron
|
- run: xvfb-run --auto-servernum npm run test-electron
|
||||||
|
|
|
@ -5476,7 +5476,7 @@ limitations under the License.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## boring 4.9.0
|
## boring 4.13.0
|
||||||
|
|
||||||
```
|
```
|
||||||
Copyright 2011-2017 Google Inc.
|
Copyright 2011-2017 Google Inc.
|
||||||
|
@ -5945,7 +5945,7 @@ express Statement of Purpose.
|
||||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
```
|
```
|
||||||
|
|
||||||
## boring-sys 4.9.0
|
## boring-sys 4.13.0
|
||||||
|
|
||||||
```
|
```
|
||||||
/* Copyright (c) 2015, Google Inc.
|
/* Copyright (c) 2015, Google Inc.
|
||||||
|
@ -6316,7 +6316,7 @@ DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## boring-sys 4.9.0
|
## boring-sys 4.13.0
|
||||||
|
|
||||||
```
|
```
|
||||||
Copyright (c) 2014 Alex Crichton
|
Copyright (c) 2014 Alex Crichton
|
||||||
|
@ -6985,7 +6985,7 @@ DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## boring-sys 4.9.0
|
## boring-sys 4.13.0
|
||||||
|
|
||||||
```
|
```
|
||||||
Copyright (c) 2015-2016 the fiat-crypto authors (see
|
Copyright (c) 2015-2016 the fiat-crypto authors (see
|
||||||
|
@ -7524,7 +7524,7 @@ SOFTWARE.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## tokio-boring 4.9.0
|
## tokio-boring 4.13.0
|
||||||
|
|
||||||
```
|
```
|
||||||
Copyright (c) 2016 Tokio contributors
|
Copyright (c) 2016 Tokio contributors
|
||||||
|
@ -9221,6 +9221,31 @@ SOFTWARE.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## openssl-macros 0.1.1
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright (c) 2022 Steven Fackler
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## inout 0.1.3
|
## inout 0.1.3
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -11102,7 +11127,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
```
|
```
|
||||||
|
|
||||||
## boring-sys 4.9.0, ring 0.17.8
|
## boring-sys 4.13.0, ring 0.17.8
|
||||||
|
|
||||||
```
|
```
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
|
|
9
package-lock.json
generated
9
package-lock.json
generated
|
@ -22,7 +22,7 @@
|
||||||
"@react-aria/utils": "3.25.3",
|
"@react-aria/utils": "3.25.3",
|
||||||
"@react-spring/web": "9.7.5",
|
"@react-spring/web": "9.7.5",
|
||||||
"@signalapp/better-sqlite3": "9.0.8",
|
"@signalapp/better-sqlite3": "9.0.8",
|
||||||
"@signalapp/libsignal-client": "0.63.0",
|
"@signalapp/libsignal-client": "0.64.1",
|
||||||
"@signalapp/ringrtc": "2.49.1",
|
"@signalapp/ringrtc": "2.49.1",
|
||||||
"@types/fabric": "4.5.3",
|
"@types/fabric": "4.5.3",
|
||||||
"backbone": "1.6.0",
|
"backbone": "1.6.0",
|
||||||
|
@ -6007,11 +6007,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@signalapp/libsignal-client": {
|
"node_modules/@signalapp/libsignal-client": {
|
||||||
"version": "0.63.0",
|
"version": "0.64.1",
|
||||||
"resolved": "https://registry.npmjs.org/@signalapp/libsignal-client/-/libsignal-client-0.63.0.tgz",
|
"resolved": "https://registry.npmjs.org/@signalapp/libsignal-client/-/libsignal-client-0.64.1.tgz",
|
||||||
"integrity": "sha512-tLzVj9NoFNk8cD05QLdlm+VzgA7Eq91K8EALiSKAAJjt4rlsXT3mouBTITnWpOgTvk7YTdAFN4K74SyUVEeYgQ==",
|
"integrity": "sha512-ex45KXdmPtTW9q4DkF/VM/K0XnuuXzsqFFkanEeUE07kEzjGfx1x9tVhQ5h2+lokVjEVLM1Oq6lVysCpk3iwcg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "AGPL-3.0-only",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-gyp-build": "^4.8.0",
|
"node-gyp-build": "^4.8.0",
|
||||||
"type-fest": "^4.26.0",
|
"type-fest": "^4.26.0",
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
"@react-aria/utils": "3.25.3",
|
"@react-aria/utils": "3.25.3",
|
||||||
"@react-spring/web": "9.7.5",
|
"@react-spring/web": "9.7.5",
|
||||||
"@signalapp/better-sqlite3": "9.0.8",
|
"@signalapp/better-sqlite3": "9.0.8",
|
||||||
"@signalapp/libsignal-client": "0.63.0",
|
"@signalapp/libsignal-client": "0.64.1",
|
||||||
"@signalapp/ringrtc": "2.49.1",
|
"@signalapp/ringrtc": "2.49.1",
|
||||||
"@types/fabric": "4.5.3",
|
"@types/fabric": "4.5.3",
|
||||||
"backbone": "1.6.0",
|
"backbone": "1.6.0",
|
||||||
|
|
|
@ -10,6 +10,8 @@ message BackupInfo {
|
||||||
uint64 version = 1;
|
uint64 version = 1;
|
||||||
uint64 backupTimeMs = 2;
|
uint64 backupTimeMs = 2;
|
||||||
bytes mediaRootBackupKey = 3; // 32-byte random value generated when the backup is uploaded for the first time.
|
bytes mediaRootBackupKey = 3; // 32-byte random value generated when the backup is uploaded for the first time.
|
||||||
|
string currentAppVersion = 4;
|
||||||
|
string firstAppVersion = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frames must follow in the following ordering rules:
|
// Frames must follow in the following ordering rules:
|
||||||
|
@ -19,9 +21,13 @@ message BackupInfo {
|
||||||
// e.g. a Recipient must come before any Chat referencing it.
|
// e.g. a Recipient must come before any Chat referencing it.
|
||||||
// 3. All ChatItems must appear in global Chat rendering order.
|
// 3. All ChatItems must appear in global Chat rendering order.
|
||||||
// (The order in which they were received by the client.)
|
// (The order in which they were received by the client.)
|
||||||
|
// 4. ChatFolders must appear in render order (e.g., left to right for
|
||||||
|
// LTR locales), but can appear anywhere relative to other frames respecting
|
||||||
|
// rule 2 (after Recipients and Chats).
|
||||||
|
//
|
||||||
|
// Recipients, Chats, StickerPacks, AdHocCalls, and NotificationProfiles
|
||||||
|
// can be in any order. (But must respect rule 2.)
|
||||||
//
|
//
|
||||||
// Recipients, Chats, StickerPacks, and AdHocCalls can be in any order.
|
|
||||||
// (But must respect rule 2.)
|
|
||||||
// For example, Chats may all be together at the beginning,
|
// For example, Chats may all be together at the beginning,
|
||||||
// or may each immediately precede its first ChatItem.
|
// or may each immediately precede its first ChatItem.
|
||||||
message Frame {
|
message Frame {
|
||||||
|
@ -32,6 +38,8 @@ message Frame {
|
||||||
ChatItem chatItem = 4;
|
ChatItem chatItem = 4;
|
||||||
StickerPack stickerPack = 5;
|
StickerPack stickerPack = 5;
|
||||||
AdHocCall adHocCall = 6;
|
AdHocCall adHocCall = 6;
|
||||||
|
NotificationProfile notificationProfile = 7;
|
||||||
|
ChatFolder chatFolder = 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +95,17 @@ message AccountData {
|
||||||
bool manuallyCancelled = 3;
|
bool manuallyCancelled = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message IAPSubscriberData {
|
||||||
|
bytes subscriberId = 1;
|
||||||
|
|
||||||
|
oneof iapSubscriptionId {
|
||||||
|
// Identifies an Android Play Store IAP subscription.
|
||||||
|
string purchaseToken = 2;
|
||||||
|
// Identifies an iOS App Store IAP subscription.
|
||||||
|
uint64 originalTransactionId = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bytes profileKey = 1;
|
bytes profileKey = 1;
|
||||||
optional string username = 2;
|
optional string username = 2;
|
||||||
UsernameLink usernameLink = 3;
|
UsernameLink usernameLink = 3;
|
||||||
|
@ -94,8 +113,9 @@ message AccountData {
|
||||||
string familyName = 5;
|
string familyName = 5;
|
||||||
string avatarUrlPath = 6;
|
string avatarUrlPath = 6;
|
||||||
SubscriberData donationSubscriberData = 7;
|
SubscriberData donationSubscriberData = 7;
|
||||||
SubscriberData backupsSubscriberData = 8;
|
reserved /*backupsSubscriberData*/ 8; // A deprecated format
|
||||||
AccountSettings accountSettings = 9;
|
AccountSettings accountSettings = 9;
|
||||||
|
IAPSubscriberData backupsSubscriberData = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Recipient {
|
message Recipient {
|
||||||
|
@ -249,7 +269,7 @@ message Chat {
|
||||||
bool archived = 3;
|
bool archived = 3;
|
||||||
optional uint32 pinnedOrder = 4; // will be displayed in ascending order
|
optional uint32 pinnedOrder = 4; // will be displayed in ascending order
|
||||||
optional uint64 expirationTimerMs = 5;
|
optional uint64 expirationTimerMs = 5;
|
||||||
optional uint64 muteUntilMs = 6; // UINT64_MAX (2^63 - 1) = "always muted".
|
optional uint64 muteUntilMs = 6; // INT64_MAX (2^63 - 1) = "always muted".
|
||||||
bool markedUnread = 7;
|
bool markedUnread = 7;
|
||||||
bool dontNotifyForMentionsIfMuted = 8;
|
bool dontNotifyForMentionsIfMuted = 8;
|
||||||
ChatStyle style = 9;
|
ChatStyle style = 9;
|
||||||
|
@ -275,7 +295,7 @@ message CallLink {
|
||||||
optional bytes adminKey = 2; // Only present if the user is an admin
|
optional bytes adminKey = 2; // Only present if the user is an admin
|
||||||
string name = 3;
|
string name = 3;
|
||||||
Restrictions restrictions = 4;
|
Restrictions restrictions = 4;
|
||||||
optional uint64 expirationMs = 5;
|
uint64 expirationMs = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AdHocCall {
|
message AdHocCall {
|
||||||
|
@ -1172,3 +1192,48 @@ message ChatStyle {
|
||||||
|
|
||||||
bool dimWallpaperInDarkMode = 7;
|
bool dimWallpaperInDarkMode = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message NotificationProfile {
|
||||||
|
enum DayOfWeek {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
MONDAY = 1;
|
||||||
|
TUESDAY = 2;
|
||||||
|
WEDNESDAY = 3;
|
||||||
|
THURSDAY = 4;
|
||||||
|
FRIDAY = 5;
|
||||||
|
SATURDAY = 6;
|
||||||
|
SUNDAY = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = 1;
|
||||||
|
optional string emoji = 2;
|
||||||
|
fixed32 color = 3; // 0xAARRGGBB
|
||||||
|
uint64 createdAtMs = 4;
|
||||||
|
bool allowAllCalls = 5;
|
||||||
|
bool allowAllMentions = 6;
|
||||||
|
repeated uint64 allowedMembers = 7; // generated recipient id for allowed groups and contacts
|
||||||
|
bool scheduleEnabled = 8;
|
||||||
|
uint32 scheduleStartTime = 9; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
|
||||||
|
uint32 scheduleEndTime = 10; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
|
||||||
|
repeated DayOfWeek scheduleDaysEnabled = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ChatFolder {
|
||||||
|
// Represents the default "All chats" folder record vs all other custom folders
|
||||||
|
enum FolderType {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
ALL = 1;
|
||||||
|
CUSTOM = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = 1;
|
||||||
|
bool showOnlyUnread = 2;
|
||||||
|
bool showMutedChats = 3;
|
||||||
|
// Folder includes all 1:1 chats, unless excluded
|
||||||
|
bool includeAllIndividualChats = 4;
|
||||||
|
// Folder includes all group chats, unless excluded
|
||||||
|
bool includeAllGroupChats = 5;
|
||||||
|
FolderType folderType = 6;
|
||||||
|
repeated uint64 includedRecipientIds = 7; // generated recipient id of groups, contacts, and/or note to self
|
||||||
|
repeated uint64 excludedRecipientIds = 8; // generated recipient id of groups, contacts, and/or note to self
|
||||||
|
}
|
|
@ -176,6 +176,17 @@ message AccountRecord {
|
||||||
optional Color color = 3; // color of the QR code itself
|
optional Color color = 3; // color of the QR code itself
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message IAPSubscriberData {
|
||||||
|
optional bytes subscriberId = 1;
|
||||||
|
|
||||||
|
oneof iapSubscriptionId {
|
||||||
|
// Identifies an Android Play Store IAP subscription.
|
||||||
|
string purchaseToken = 2;
|
||||||
|
// Identifies an iOS App Store IAP subscription.
|
||||||
|
uint64 originalTransactionId = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
optional bytes profileKey = 1;
|
optional bytes profileKey = 1;
|
||||||
optional string givenName = 2;
|
optional string givenName = 2;
|
||||||
optional string familyName = 3;
|
optional string familyName = 3;
|
||||||
|
@ -210,9 +221,14 @@ message AccountRecord {
|
||||||
optional string username = 33;
|
optional string username = 33;
|
||||||
optional bool hasCompletedUsernameOnboarding = 34;
|
optional bool hasCompletedUsernameOnboarding = 34;
|
||||||
optional UsernameLink usernameLink = 35;
|
optional UsernameLink usernameLink = 35;
|
||||||
optional bytes backupsSubscriberId = 36;
|
reserved /*backupsSubscriberId*/ 36;
|
||||||
optional string backupsSubscriberCurrencyCode = 37;
|
reserved /*backupsSubscriberCurrencyCode*/ 37;
|
||||||
optional bool backupsSubscriptionManuallyCancelled = 38;
|
reserved /*backupsSubscriptionManuallyCancelled*/ 38;
|
||||||
|
// Set to true after backups are enabled and one is uploaded.
|
||||||
|
optional bool hasBackup = 39;
|
||||||
|
// See zkgroup for integer particular values
|
||||||
|
optional uint64 backupTier = 40;
|
||||||
|
optional IAPSubscriberData backupSubscriberData = 41;
|
||||||
}
|
}
|
||||||
|
|
||||||
message StoryDistributionListRecord {
|
message StoryDistributionListRecord {
|
||||||
|
|
|
@ -139,6 +139,7 @@ import { getRoomIdFromRootKey } from '../../util/callLinksRingrtc';
|
||||||
import { SeenStatus } from '../../MessageSeenStatus';
|
import { SeenStatus } from '../../MessageSeenStatus';
|
||||||
import { migrateAllMessages } from '../../messages/migrateMessageData';
|
import { migrateAllMessages } from '../../messages/migrateMessageData';
|
||||||
import { trimBody } from '../../util/longAttachment';
|
import { trimBody } from '../../util/longAttachment';
|
||||||
|
import { generateBackupsSubscriberData } from '../../util/backupSubscriptionData';
|
||||||
|
|
||||||
const MAX_CONCURRENCY = 10;
|
const MAX_CONCURRENCY = 10;
|
||||||
|
|
||||||
|
@ -258,6 +259,8 @@ export class BackupExportStream extends Readable {
|
||||||
version: Long.fromNumber(BACKUP_VERSION),
|
version: Long.fromNumber(BACKUP_VERSION),
|
||||||
backupTimeMs: this.backupTimeMs,
|
backupTimeMs: this.backupTimeMs,
|
||||||
mediaRootBackupKey: getBackupMediaRootKey().serialize(),
|
mediaRootBackupKey: getBackupMediaRootKey().serialize(),
|
||||||
|
firstAppVersion: window.storage.get('restoredBackupFirstAppVersion'),
|
||||||
|
currentAppVersion: `Desktop ${window.getVersion()}`,
|
||||||
}).finish()
|
}).finish()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -660,7 +663,8 @@ export class BackupExportStream extends Readable {
|
||||||
const usernameLink = storage.get('usernameLink');
|
const usernameLink = storage.get('usernameLink');
|
||||||
|
|
||||||
const subscriberId = storage.get('subscriberId');
|
const subscriberId = storage.get('subscriberId');
|
||||||
const backupsSubscriberId = storage.get('backupsSubscriberId');
|
|
||||||
|
const backupsSubscriberData = generateBackupsSubscriberData();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
profileKey: storage.get('profileKey'),
|
profileKey: storage.get('profileKey'),
|
||||||
|
@ -676,16 +680,7 @@ export class BackupExportStream extends Readable {
|
||||||
givenName: me.get('profileName'),
|
givenName: me.get('profileName'),
|
||||||
familyName: me.get('profileFamilyName'),
|
familyName: me.get('profileFamilyName'),
|
||||||
avatarUrlPath: storage.get('avatarUrl'),
|
avatarUrlPath: storage.get('avatarUrl'),
|
||||||
backupsSubscriberData: Bytes.isNotEmpty(backupsSubscriberId)
|
backupsSubscriberData,
|
||||||
? {
|
|
||||||
subscriberId: backupsSubscriberId,
|
|
||||||
currencyCode: storage.get('backupsSubscriberCurrencyCode'),
|
|
||||||
manuallyCancelled: storage.get(
|
|
||||||
'backupsSubscriptionManuallyCancelled',
|
|
||||||
false
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
donationSubscriberData: Bytes.isNotEmpty(subscriberId)
|
donationSubscriberData: Bytes.isNotEmpty(subscriberId)
|
||||||
? {
|
? {
|
||||||
subscriberId,
|
subscriberId,
|
||||||
|
|
|
@ -122,6 +122,7 @@ import { hasAttachmentDownloads } from '../../util/hasAttachmentDownloads';
|
||||||
import { isNightly } from '../../util/version';
|
import { isNightly } from '../../util/version';
|
||||||
import { ToastType } from '../../types/Toast';
|
import { ToastType } from '../../types/Toast';
|
||||||
import { isConversationAccepted } from '../../util/isConversationAccepted';
|
import { isConversationAccepted } from '../../util/isConversationAccepted';
|
||||||
|
import { saveBackupsSubscriberData } from '../../util/backupSubscriptionData';
|
||||||
|
|
||||||
const MAX_CONCURRENCY = 10;
|
const MAX_CONCURRENCY = 10;
|
||||||
|
|
||||||
|
@ -262,6 +263,11 @@ export class BackupImportStream extends Writable {
|
||||||
throw new Error('Missing mediaRootBackupKey');
|
throw new Error('Missing mediaRootBackupKey');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await window.storage.put(
|
||||||
|
'restoredBackupFirstAppVersion',
|
||||||
|
info.firstAppVersion
|
||||||
|
);
|
||||||
|
|
||||||
const theirKey = info.mediaRootBackupKey;
|
const theirKey = info.mediaRootBackupKey;
|
||||||
const ourKey = getBackupMediaRootKey().serialize();
|
const ourKey = getBackupMediaRootKey().serialize();
|
||||||
if (!constantTimeEqual(theirKey, ourKey)) {
|
if (!constantTimeEqual(theirKey, ourKey)) {
|
||||||
|
@ -658,22 +664,8 @@ export class BackupImportStream extends Writable {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (backupsSubscriberData != null) {
|
|
||||||
const { subscriberId, currencyCode, manuallyCancelled } =
|
await saveBackupsSubscriberData(backupsSubscriberData);
|
||||||
backupsSubscriberData;
|
|
||||||
if (Bytes.isNotEmpty(subscriberId)) {
|
|
||||||
await storage.put('backupsSubscriberId', subscriberId);
|
|
||||||
}
|
|
||||||
if (currencyCode != null) {
|
|
||||||
await storage.put('backupsSubscriberCurrencyCode', currencyCode);
|
|
||||||
}
|
|
||||||
if (manuallyCancelled != null) {
|
|
||||||
await storage.put(
|
|
||||||
'backupsSubscriptionManuallyCancelled',
|
|
||||||
manuallyCancelled
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await storage.put(
|
await storage.put(
|
||||||
'read-receipt-setting',
|
'read-receipt-setting',
|
||||||
|
@ -1213,6 +1205,7 @@ export class BackupImportStream extends Writable {
|
||||||
if (conversation.active_at == null) {
|
if (conversation.active_at == null) {
|
||||||
conversation.active_at = Math.max(chat.id.toNumber(), 1);
|
conversation.active_at = Math.max(chat.id.toNumber(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
conversation.isArchived = chat.archived === true;
|
conversation.isArchived = chat.archived === true;
|
||||||
conversation.isPinned = (chat.pinnedOrder || 0) !== 0;
|
conversation.isPinned = (chat.pinnedOrder || 0) !== 0;
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,10 @@ import { fromAdminKeyBytes, toAdminKeyBytes } from '../util/callLinks';
|
||||||
import { isOlderThan } from '../util/timestamp';
|
import { isOlderThan } from '../util/timestamp';
|
||||||
import { getMessageQueueTime } from '../util/getMessageQueueTime';
|
import { getMessageQueueTime } from '../util/getMessageQueueTime';
|
||||||
import { callLinkRefreshJobQueue } from '../jobs/callLinkRefreshJobQueue';
|
import { callLinkRefreshJobQueue } from '../jobs/callLinkRefreshJobQueue';
|
||||||
|
import {
|
||||||
|
generateBackupsSubscriberData,
|
||||||
|
saveBackupsSubscriberData,
|
||||||
|
} from '../util/backupSubscriptionData';
|
||||||
|
|
||||||
const MY_STORY_BYTES = uuidToBytes(MY_STORY_ID);
|
const MY_STORY_BYTES = uuidToBytes(MY_STORY_ID);
|
||||||
|
|
||||||
|
@ -406,9 +410,7 @@ export function toAccountRecord(
|
||||||
if (Bytes.isNotEmpty(subscriberId)) {
|
if (Bytes.isNotEmpty(subscriberId)) {
|
||||||
accountRecord.subscriberId = subscriberId;
|
accountRecord.subscriberId = subscriberId;
|
||||||
}
|
}
|
||||||
const subscriberCurrencyCode = window.storage.get(
|
const subscriberCurrencyCode = window.storage.get('subscriberCurrencyCode');
|
||||||
'backupsSubscriberCurrencyCode'
|
|
||||||
);
|
|
||||||
if (typeof subscriberCurrencyCode === 'string') {
|
if (typeof subscriberCurrencyCode === 'string') {
|
||||||
accountRecord.subscriberCurrencyCode = subscriberCurrencyCode;
|
accountRecord.subscriberCurrencyCode = subscriberCurrencyCode;
|
||||||
}
|
}
|
||||||
|
@ -419,23 +421,9 @@ export function toAccountRecord(
|
||||||
accountRecord.donorSubscriptionManuallyCancelled =
|
accountRecord.donorSubscriptionManuallyCancelled =
|
||||||
donorSubscriptionManuallyCancelled;
|
donorSubscriptionManuallyCancelled;
|
||||||
}
|
}
|
||||||
const backupsSubscriberId = window.storage.get('backupsSubscriberId');
|
|
||||||
if (Bytes.isNotEmpty(backupsSubscriberId)) {
|
accountRecord.backupSubscriberData = generateBackupsSubscriberData();
|
||||||
accountRecord.backupsSubscriberId = backupsSubscriberId;
|
|
||||||
}
|
|
||||||
const backupsSubscriberCurrencyCode = window.storage.get(
|
|
||||||
'backupsSubscriberCurrencyCode'
|
|
||||||
);
|
|
||||||
if (typeof backupsSubscriberCurrencyCode === 'string') {
|
|
||||||
accountRecord.backupsSubscriberCurrencyCode = backupsSubscriberCurrencyCode;
|
|
||||||
}
|
|
||||||
const backupsSubscriptionManuallyCancelled = window.storage.get(
|
|
||||||
'backupsSubscriptionManuallyCancelled'
|
|
||||||
);
|
|
||||||
if (typeof backupsSubscriptionManuallyCancelled === 'boolean') {
|
|
||||||
accountRecord.backupsSubscriptionManuallyCancelled =
|
|
||||||
backupsSubscriptionManuallyCancelled;
|
|
||||||
}
|
|
||||||
const displayBadgesOnProfile = window.storage.get('displayBadgesOnProfile');
|
const displayBadgesOnProfile = window.storage.get('displayBadgesOnProfile');
|
||||||
if (displayBadgesOnProfile !== undefined) {
|
if (displayBadgesOnProfile !== undefined) {
|
||||||
accountRecord.displayBadgesOnProfile = displayBadgesOnProfile;
|
accountRecord.displayBadgesOnProfile = displayBadgesOnProfile;
|
||||||
|
@ -1327,9 +1315,7 @@ export async function mergeAccountRecord(
|
||||||
subscriberId,
|
subscriberId,
|
||||||
subscriberCurrencyCode,
|
subscriberCurrencyCode,
|
||||||
donorSubscriptionManuallyCancelled,
|
donorSubscriptionManuallyCancelled,
|
||||||
backupsSubscriberId,
|
backupSubscriberData,
|
||||||
backupsSubscriberCurrencyCode,
|
|
||||||
backupsSubscriptionManuallyCancelled,
|
|
||||||
displayBadgesOnProfile,
|
displayBadgesOnProfile,
|
||||||
keepMutedChatsArchived,
|
keepMutedChatsArchived,
|
||||||
hasCompletedUsernameOnboarding,
|
hasCompletedUsernameOnboarding,
|
||||||
|
@ -1548,21 +1534,9 @@ export async function mergeAccountRecord(
|
||||||
donorSubscriptionManuallyCancelled
|
donorSubscriptionManuallyCancelled
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (Bytes.isNotEmpty(backupsSubscriberId)) {
|
|
||||||
await window.storage.put('backupsSubscriberId', backupsSubscriberId);
|
await saveBackupsSubscriberData(backupSubscriberData);
|
||||||
}
|
|
||||||
if (typeof backupsSubscriberCurrencyCode === 'string') {
|
|
||||||
await window.storage.put(
|
|
||||||
'backupsSubscriberCurrencyCode',
|
|
||||||
backupsSubscriberCurrencyCode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (backupsSubscriptionManuallyCancelled != null) {
|
|
||||||
await window.storage.put(
|
|
||||||
'backupsSubscriptionManuallyCancelled',
|
|
||||||
backupsSubscriptionManuallyCancelled
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await window.storage.put(
|
await window.storage.put(
|
||||||
'displayBadgesOnProfile',
|
'displayBadgesOnProfile',
|
||||||
Boolean(displayBadgesOnProfile)
|
Boolean(displayBadgesOnProfile)
|
||||||
|
|
|
@ -60,6 +60,12 @@ describe('backup/integration', () => {
|
||||||
|
|
||||||
const files = readdirSync(BACKUP_INTEGRATION_DIR)
|
const files = readdirSync(BACKUP_INTEGRATION_DIR)
|
||||||
.filter(file => file.endsWith('.binproto'))
|
.filter(file => file.endsWith('.binproto'))
|
||||||
|
.filter(
|
||||||
|
file =>
|
||||||
|
// TODO (DESKTOP-8025)
|
||||||
|
!file.startsWith('chat_folder_') &&
|
||||||
|
!file.startsWith('notification_profile_')
|
||||||
|
)
|
||||||
.map(file => join(BACKUP_INTEGRATION_DIR, file));
|
.map(file => join(BACKUP_INTEGRATION_DIR, file));
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
|
|
7
ts/types/Storage.d.ts
vendored
7
ts/types/Storage.d.ts
vendored
|
@ -175,8 +175,8 @@ export type StorageAccessType = {
|
||||||
subscriberCurrencyCode: string;
|
subscriberCurrencyCode: string;
|
||||||
donorSubscriptionManuallyCancelled: boolean;
|
donorSubscriptionManuallyCancelled: boolean;
|
||||||
backupsSubscriberId: Uint8Array;
|
backupsSubscriberId: Uint8Array;
|
||||||
backupsSubscriberCurrencyCode: string;
|
backupsSubscriberPurchaseToken: string;
|
||||||
backupsSubscriptionManuallyCancelled: boolean;
|
backupsSubscriberOriginalTransactionId: string;
|
||||||
displayBadgesOnProfile: boolean;
|
displayBadgesOnProfile: boolean;
|
||||||
keepMutedChatsArchived: boolean;
|
keepMutedChatsArchived: boolean;
|
||||||
usernameLastIntegrityCheck: number;
|
usernameLastIntegrityCheck: number;
|
||||||
|
@ -209,6 +209,9 @@ export type StorageAccessType = {
|
||||||
// If true Desktop message history was restored from backup
|
// If true Desktop message history was restored from backup
|
||||||
isRestoredFromBackup: boolean;
|
isRestoredFromBackup: boolean;
|
||||||
|
|
||||||
|
// The `firstAppVersion` present on an BackupInfo from an imported backup.
|
||||||
|
restoredBackupFirstAppVersion: string;
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
'challenge:retry-message-ids': never;
|
'challenge:retry-message-ids': never;
|
||||||
nextSignedKeyRotationTime: number;
|
nextSignedKeyRotationTime: number;
|
||||||
|
|
75
ts/util/backupSubscriptionData.ts
Normal file
75
ts/util/backupSubscriptionData.ts
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2024 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import Long from 'long';
|
||||||
|
import type { Backups, SignalService } from '../protobuf';
|
||||||
|
import * as Bytes from '../Bytes';
|
||||||
|
|
||||||
|
// These two proto messages (Backups.AccountData.IIAPSubscriberData &&
|
||||||
|
// SignalService.AccountRecord.IIAPSubscriberData) should remain in sync. If they drift,
|
||||||
|
// we'll need separate logic for each
|
||||||
|
export async function saveBackupsSubscriberData(
|
||||||
|
backupsSubscriberData:
|
||||||
|
| Backups.AccountData.IIAPSubscriberData
|
||||||
|
| SignalService.AccountRecord.IIAPSubscriberData
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
): Promise<void> {
|
||||||
|
if (backupsSubscriberData == null) {
|
||||||
|
await window.storage.remove('backupsSubscriberId');
|
||||||
|
await window.storage.remove('backupsSubscriberPurchaseToken');
|
||||||
|
await window.storage.remove('backupsSubscriberOriginalTransactionId');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { subscriberId, purchaseToken, originalTransactionId } =
|
||||||
|
backupsSubscriberData;
|
||||||
|
|
||||||
|
if (Bytes.isNotEmpty(subscriberId)) {
|
||||||
|
await window.storage.put('backupsSubscriberId', subscriberId);
|
||||||
|
} else {
|
||||||
|
await window.storage.remove('backupsSubscriberId');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (purchaseToken) {
|
||||||
|
await window.storage.put('backupsSubscriberPurchaseToken', purchaseToken);
|
||||||
|
} else {
|
||||||
|
await window.storage.remove('backupsSubscriberPurchaseToken');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalTransactionId) {
|
||||||
|
await window.storage.put(
|
||||||
|
'backupsSubscriberOriginalTransactionId',
|
||||||
|
originalTransactionId.toString()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await window.storage.remove('backupsSubscriberOriginalTransactionId');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateBackupsSubscriberData(): Backups.AccountData.IIAPSubscriberData | null {
|
||||||
|
const backupsSubscriberId = window.storage.get('backupsSubscriberId');
|
||||||
|
|
||||||
|
if (Bytes.isEmpty(backupsSubscriberId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backupsSubscriberData: Backups.AccountData.IIAPSubscriberData = {
|
||||||
|
subscriberId: backupsSubscriberId,
|
||||||
|
};
|
||||||
|
const purchaseToken = window.storage.get('backupsSubscriberPurchaseToken');
|
||||||
|
if (purchaseToken) {
|
||||||
|
backupsSubscriberData.purchaseToken = purchaseToken;
|
||||||
|
} else {
|
||||||
|
const originalTransactionId = window.storage.get(
|
||||||
|
'backupsSubscriberOriginalTransactionId'
|
||||||
|
);
|
||||||
|
if (originalTransactionId) {
|
||||||
|
backupsSubscriberData.originalTransactionId = Long.fromString(
|
||||||
|
originalTransactionId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return backupsSubscriberData;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue