mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-24 20:26:24 +00:00
Fix some backup export issues
This commit is contained in:
@@ -194,6 +194,7 @@ export default {
|
||||
return {
|
||||
result: {
|
||||
totalBytes: 100,
|
||||
duration: 10000,
|
||||
stats: {
|
||||
adHocCalls: 1,
|
||||
callLinks: 2,
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { useState, useCallback } from 'react';
|
||||
import type { LocalizerType } from '../types/I18N';
|
||||
import { toLogFormat } from '../types/errors';
|
||||
import { formatFileSize } from '../util/formatFileSize';
|
||||
import { SECOND } from '../util/durations';
|
||||
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
||||
import { SettingsRow, SettingsControl } from './PreferencesUtil';
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
@@ -38,12 +39,13 @@ export function PreferencesInternal({
|
||||
if (validationResult != null) {
|
||||
if ('result' in validationResult) {
|
||||
const {
|
||||
result: { totalBytes, stats },
|
||||
result: { totalBytes, stats, duration },
|
||||
} = validationResult;
|
||||
|
||||
validationElem = (
|
||||
<div className="Preferences--internal--validate-backup--result">
|
||||
<p>File size: {formatFileSize(totalBytes)}</p>
|
||||
<p>Duration: {Math.round(duration / SECOND)}s</p>
|
||||
<pre>
|
||||
<code>{JSON.stringify(stats, null, 2)}</code>
|
||||
</pre>
|
||||
|
||||
@@ -543,6 +543,11 @@ export class BackupExportStream extends Readable {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status === AdhocCallStatus.Deleted) {
|
||||
log.info(`backups: Dropping deleted ad-hoc call ${callId.slice(-2)}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.#pushFrame({
|
||||
adHocCall: {
|
||||
callId: Long.fromString(callId),
|
||||
@@ -1029,7 +1034,7 @@ export class BackupExportStream extends Readable {
|
||||
if (
|
||||
conversation &&
|
||||
isGroupV2(conversation.attributes) &&
|
||||
message.storyReplyContext
|
||||
(message.storyReplyContext || message.storyReaction)
|
||||
) {
|
||||
// We drop group story replies
|
||||
return undefined;
|
||||
@@ -1526,10 +1531,14 @@ export class BackupExportStream extends Readable {
|
||||
simpleUpdate.type = Backups.SimpleChatUpdate.Type.IDENTITY_UPDATE;
|
||||
|
||||
if (message.key_changed) {
|
||||
const target = window.ConversationController.get(message.key_changed);
|
||||
if (!target) {
|
||||
throw new Error(
|
||||
'toChatItemUpdate/keyCahnge: key_changed conversation not found!'
|
||||
);
|
||||
}
|
||||
// This will override authorId on the original chatItem
|
||||
patch.authorId = this.#getOrPushPrivateRecipient({
|
||||
id: message.key_changed,
|
||||
});
|
||||
patch.authorId = this.#getOrPushPrivateRecipient(target.attributes);
|
||||
}
|
||||
|
||||
updateMessage.simpleUpdate = simpleUpdate;
|
||||
@@ -2618,7 +2627,6 @@ export class BackupExportStream extends Readable {
|
||||
| 'bodyAttachment'
|
||||
| 'bodyRanges'
|
||||
| 'storyReaction'
|
||||
| 'storyReplyContext'
|
||||
| 'received_at'
|
||||
| 'reactions'
|
||||
>;
|
||||
|
||||
@@ -3283,7 +3283,7 @@ export class BackupImportStream extends Writable {
|
||||
case Type.IDENTITY_UPDATE:
|
||||
return {
|
||||
type: 'keychange',
|
||||
key_changed: isGroup(conversation) ? author?.id : undefined,
|
||||
key_changed: isGroup(conversation) ? author?.serviceId : undefined,
|
||||
};
|
||||
case Type.IDENTITY_VERIFIED:
|
||||
strictAssert(author != null, 'IDENTITY_VERIFIED must have an author');
|
||||
|
||||
@@ -100,6 +100,7 @@ export type ImportOptionsType = Readonly<{
|
||||
|
||||
export type ExportResultType = Readonly<{
|
||||
totalBytes: number;
|
||||
duration: number;
|
||||
stats: Readonly<StatsType>;
|
||||
}>;
|
||||
|
||||
@@ -767,6 +768,7 @@ export class BackupsService {
|
||||
log.info('exportBackup: starting...');
|
||||
this.#isRunning = 'export';
|
||||
|
||||
const start = Date.now();
|
||||
try {
|
||||
// TODO (DESKTOP-7168): Update mock-server to support this endpoint
|
||||
if (window.SignalCI || backupType === BackupType.TestOnlyPlaintext) {
|
||||
@@ -815,7 +817,8 @@ export class BackupsService {
|
||||
throw missingCaseError(backupType);
|
||||
}
|
||||
|
||||
return { totalBytes, stats: recordStream.getStats() };
|
||||
const duration = Date.now() - start;
|
||||
return { totalBytes, stats: recordStream.getStats(), duration };
|
||||
} finally {
|
||||
log.info('exportBackup: finished...');
|
||||
this.#isRunning = false;
|
||||
|
||||
@@ -40,7 +40,6 @@ import { bytesToUuid } from '../../../util/uuidToBytes';
|
||||
import { createName } from '../../../util/attachmentPath';
|
||||
import { ensureAttachmentIsReencryptable } from '../../../util/ensureAttachmentIsReencryptable';
|
||||
import type { ReencryptionInfo } from '../../../AttachmentCrypto';
|
||||
import { dropZero } from '../../../util/dropZero';
|
||||
|
||||
export function convertFilePointerToAttachment(
|
||||
filePointer: Backups.FilePointer,
|
||||
@@ -70,13 +69,16 @@ export function convertFilePointerToAttachment(
|
||||
fileName: fileName ?? undefined,
|
||||
caption: caption ?? undefined,
|
||||
blurHash: blurHash ?? undefined,
|
||||
incrementalMac: incrementalMac?.length
|
||||
? Bytes.toBase64(incrementalMac)
|
||||
: undefined,
|
||||
chunkSize: dropZero(incrementalMacChunkSize),
|
||||
incrementalMac: undefined,
|
||||
chunkSize: undefined,
|
||||
downloadPath: doCreateName(),
|
||||
};
|
||||
|
||||
if (incrementalMac?.length && incrementalMacChunkSize) {
|
||||
commonProps.incrementalMac = Bytes.toBase64(incrementalMac);
|
||||
commonProps.chunkSize = incrementalMacChunkSize;
|
||||
}
|
||||
|
||||
if (attachmentLocator) {
|
||||
const { cdnKey, cdnNumber, key, digest, uploadTimestamp, size } =
|
||||
attachmentLocator;
|
||||
@@ -180,17 +182,22 @@ export async function getFilePointerForAttachment({
|
||||
}> {
|
||||
const filePointerRootProps = new Backups.FilePointer({
|
||||
contentType: attachment.contentType,
|
||||
// Resilience to invalid data in the database from internal testing
|
||||
incrementalMac:
|
||||
typeof attachment.incrementalMac === 'string'
|
||||
? Bytes.fromBase64(attachment.incrementalMac)
|
||||
: undefined,
|
||||
incrementalMacChunkSize: dropZero(attachment.chunkSize),
|
||||
fileName: attachment.fileName,
|
||||
width: attachment.width,
|
||||
height: attachment.height,
|
||||
caption: attachment.caption,
|
||||
blurHash: attachment.blurHash,
|
||||
|
||||
// Resilience to invalid data in the database from internal testing
|
||||
...(typeof attachment.incrementalMac === 'string' && attachment.chunkSize
|
||||
? {
|
||||
incrementalMac: Bytes.fromBase64(attachment.incrementalMac),
|
||||
incrementalMacChunkSize: attachment.chunkSize,
|
||||
}
|
||||
: {
|
||||
incrementalMac: undefined,
|
||||
incrementalMacChunkSize: undefined,
|
||||
}),
|
||||
});
|
||||
const logId = `getFilePointerForAttachment(${redactGenericText(
|
||||
attachment.digest ?? ''
|
||||
|
||||
@@ -65,7 +65,6 @@ function sortAndNormalize(
|
||||
changedId,
|
||||
conversationId,
|
||||
editHistory,
|
||||
key_changed: keyChanged,
|
||||
reactions,
|
||||
sendStateByConversationId,
|
||||
verifiedChanged,
|
||||
@@ -132,7 +131,6 @@ function sortAndNormalize(
|
||||
};
|
||||
}),
|
||||
changedId: mapConvoId(changedId),
|
||||
key_changed: mapConvoId(keyChanged),
|
||||
verifiedChanged: mapConvoId(verifiedChanged),
|
||||
sendStateByConverationId: mapSendState(sendStateByConversationId),
|
||||
editHistory: editHistory?.map(history => {
|
||||
|
||||
@@ -112,13 +112,13 @@ describe('backup/non-bubble messages', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('roundtrips IDENTITY_CHANGE update in groups', async () => {
|
||||
it('roundtrips ACI IDENTITY_CHANGE update in groups', async () => {
|
||||
await symmetricRoundtripHarness([
|
||||
{
|
||||
conversationId: group.id,
|
||||
id: generateGuid(),
|
||||
type: 'keychange',
|
||||
key_changed: contactA.id,
|
||||
key_changed: contactA.getAci(),
|
||||
received_at: 1,
|
||||
sent_at: 1,
|
||||
timestamp: 1,
|
||||
@@ -129,6 +129,39 @@ describe('backup/non-bubble messages', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('roundtrips id IDENTITY_CHANGE update in groups', async () => {
|
||||
await asymmetricRoundtripHarness(
|
||||
[
|
||||
{
|
||||
conversationId: group.id,
|
||||
id: generateGuid(),
|
||||
type: 'keychange',
|
||||
key_changed: contactA.id,
|
||||
received_at: 1,
|
||||
sent_at: 1,
|
||||
timestamp: 1,
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Seen,
|
||||
sourceServiceId: CONTACT_A,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
conversationId: group.id,
|
||||
id: generateGuid(),
|
||||
type: 'keychange',
|
||||
key_changed: contactA.getAci(),
|
||||
received_at: 1,
|
||||
sent_at: 1,
|
||||
timestamp: 1,
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Seen,
|
||||
sourceServiceId: CONTACT_A,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it('roundtrips IDENTITY_DEFAULT simple update', async () => {
|
||||
await symmetricRoundtripHarness([
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user