Export/import verified state
This commit is contained in:
parent
b517bb817f
commit
104995e980
3 changed files with 83 additions and 23 deletions
|
@ -9,8 +9,7 @@ option java_package = "org.thoughtcrime.securesms.backup.v2.proto";
|
||||||
message BackupInfo {
|
message BackupInfo {
|
||||||
uint64 version = 1;
|
uint64 version = 1;
|
||||||
uint64 backupTimeMs = 2;
|
uint64 backupTimeMs = 2;
|
||||||
bytes mediaRootBackupKey = 3; // 32-byte random value generated when the
|
bytes mediaRootBackupKey = 3; // 32-byte random value generated when the backup is uploaded for the first time.
|
||||||
// backup is uploaded for the first time.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frames must follow in the following ordering rules:
|
// Frames must follow in the following ordering rules:
|
||||||
|
@ -112,6 +111,12 @@ message Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
message Contact {
|
message Contact {
|
||||||
|
enum IdentityState {
|
||||||
|
DEFAULT = 0;
|
||||||
|
VERIFIED = 1;
|
||||||
|
UNVERIFIED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message Registered { }
|
message Registered { }
|
||||||
message NotRegistered {
|
message NotRegistered {
|
||||||
uint64 unregisteredTimestamp = 1;
|
uint64 unregisteredTimestamp = 1;
|
||||||
|
@ -140,6 +145,8 @@ message Contact {
|
||||||
optional string profileGivenName = 11;
|
optional string profileGivenName = 11;
|
||||||
optional string profileFamilyName = 12;
|
optional string profileFamilyName = 12;
|
||||||
bool hideStory = 13;
|
bool hideStory = 13;
|
||||||
|
optional bytes identityKey = 14;
|
||||||
|
IdentityState identityState = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Group {
|
message Group {
|
||||||
|
@ -240,9 +247,9 @@ message Chat {
|
||||||
uint64 id = 1; // generated id for reference only within this file
|
uint64 id = 1; // generated id for reference only within this file
|
||||||
uint64 recipientId = 2;
|
uint64 recipientId = 2;
|
||||||
bool archived = 3;
|
bool archived = 3;
|
||||||
uint32 pinnedOrder = 4; // 0 = unpinned, otherwise chat is considered pinned and will be displayed in ascending order
|
optional uint32 pinnedOrder = 4; // will be displayed in ascending order
|
||||||
uint64 expirationTimerMs = 5; // 0 = no expire timer.
|
optional uint64 expirationTimerMs = 5;
|
||||||
uint64 muteUntilMs = 6;
|
optional uint64 muteUntilMs = 6; // UINT64_MAX (2^63 - 1) = "always muted".
|
||||||
bool markedUnread = 7;
|
bool markedUnread = 7;
|
||||||
bool dontNotifyForMentionsIfMuted = 8;
|
bool dontNotifyForMentionsIfMuted = 8;
|
||||||
ChatStyle style = 9;
|
ChatStyle style = 9;
|
||||||
|
@ -268,7 +275,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;
|
||||||
uint64 expirationMs = 5;
|
optional uint64 expirationMs = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AdHocCall {
|
message AdHocCall {
|
||||||
|
@ -285,6 +292,8 @@ message AdHocCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
message DistributionListItem {
|
message DistributionListItem {
|
||||||
|
// distribution ids are UUIDv4s. "My Story" is represented
|
||||||
|
// by an all-0 UUID (00000000-0000-0000-0000-000000000000).
|
||||||
bytes distributionId = 1; // distribution list ids are uuids
|
bytes distributionId = 1; // distribution list ids are uuids
|
||||||
|
|
||||||
oneof item {
|
oneof item {
|
||||||
|
@ -310,7 +319,7 @@ message DistributionList {
|
||||||
message ChatItem {
|
message ChatItem {
|
||||||
message IncomingMessageDetails {
|
message IncomingMessageDetails {
|
||||||
uint64 dateReceived = 1;
|
uint64 dateReceived = 1;
|
||||||
uint64 dateServerSent = 2;
|
optional uint64 dateServerSent = 2;
|
||||||
bool read = 3;
|
bool read = 3;
|
||||||
bool sealedSender = 4;
|
bool sealedSender = 4;
|
||||||
}
|
}
|
||||||
|
@ -325,8 +334,8 @@ message ChatItem {
|
||||||
uint64 chatId = 1; // conversation id
|
uint64 chatId = 1; // conversation id
|
||||||
uint64 authorId = 2; // recipient id
|
uint64 authorId = 2; // recipient id
|
||||||
uint64 dateSent = 3;
|
uint64 dateSent = 3;
|
||||||
uint64 expireStartDate = 4; // timestamp of when expiration timer started ticking down
|
optional uint64 expireStartDate = 4; // timestamp of when expiration timer started ticking down
|
||||||
uint64 expiresInMs = 5; // how long timer of message is (ms)
|
optional uint64 expiresInMs = 5; // how long timer of message is (ms)
|
||||||
repeated ChatItem revisions = 6; // ordered from oldest to newest
|
repeated ChatItem revisions = 6; // ordered from oldest to newest
|
||||||
bool sms = 7;
|
bool sms = 7;
|
||||||
|
|
||||||
|
@ -616,7 +625,7 @@ message FilePointer {
|
||||||
message AttachmentLocator {
|
message AttachmentLocator {
|
||||||
string cdnKey = 1;
|
string cdnKey = 1;
|
||||||
uint32 cdnNumber = 2;
|
uint32 cdnNumber = 2;
|
||||||
uint64 uploadTimestamp = 3;
|
optional uint64 uploadTimestamp = 3;
|
||||||
bytes key = 4;
|
bytes key = 4;
|
||||||
bytes digest = 5;
|
bytes digest = 5;
|
||||||
uint32 size = 6;
|
uint32 size = 6;
|
||||||
|
@ -650,7 +659,8 @@ message Quote {
|
||||||
enum Type {
|
enum Type {
|
||||||
UNKNOWN = 0;
|
UNKNOWN = 0;
|
||||||
NORMAL = 1;
|
NORMAL = 1;
|
||||||
GIFTBADGE = 2;
|
GIFT_BADGE = 2;
|
||||||
|
VIEW_ONCE = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message QuotedAttachment {
|
message QuotedAttachment {
|
||||||
|
@ -768,8 +778,7 @@ message GroupCall {
|
||||||
optional uint64 ringerRecipientId = 3;
|
optional uint64 ringerRecipientId = 3;
|
||||||
optional uint64 startedCallRecipientId = 4;
|
optional uint64 startedCallRecipientId = 4;
|
||||||
uint64 startedCallTimestamp = 5;
|
uint64 startedCallTimestamp = 5;
|
||||||
// The time the call ended. 0 indicates an unknown time.
|
optional uint64 endedCallTimestamp = 6; // The time the call ended.
|
||||||
uint64 endedCallTimestamp = 6;
|
|
||||||
bool read = 7;
|
bool read = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,7 +834,6 @@ message SessionSwitchoverChatUpdate {
|
||||||
|
|
||||||
message GroupChangeChatUpdate {
|
message GroupChangeChatUpdate {
|
||||||
message Update {
|
message Update {
|
||||||
// Note: group expiration timer changes are represented as ExpirationTimerChatUpdate.
|
|
||||||
oneof update {
|
oneof update {
|
||||||
GenericGroupUpdate genericGroupUpdate = 1;
|
GenericGroupUpdate genericGroupUpdate = 1;
|
||||||
GroupCreationUpdate groupCreationUpdate = 2;
|
GroupCreationUpdate groupCreationUpdate = 2;
|
||||||
|
|
|
@ -17,7 +17,10 @@ import {
|
||||||
pauseWriteAccess,
|
pauseWriteAccess,
|
||||||
resumeWriteAccess,
|
resumeWriteAccess,
|
||||||
} from '../../sql/Client';
|
} from '../../sql/Client';
|
||||||
import type { PageMessagesCursorType } from '../../sql/Interface';
|
import type {
|
||||||
|
PageMessagesCursorType,
|
||||||
|
IdentityKeyType,
|
||||||
|
} from '../../sql/Interface';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { GiftBadgeStates } from '../../components/conversation/Message';
|
import { GiftBadgeStates } from '../../components/conversation/Message';
|
||||||
import { type CustomColorType } from '../../types/Colors';
|
import { type CustomColorType } from '../../types/Colors';
|
||||||
|
@ -290,6 +293,13 @@ export class BackupExportStream extends Readable {
|
||||||
stickerPacks: 0,
|
stickerPacks: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const identityKeys = await DataReader.getAllIdentityKeys();
|
||||||
|
const identityKeysById = new Map(
|
||||||
|
identityKeys.map(key => {
|
||||||
|
return [key.id, key];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
for (const { attributes } of window.ConversationController.getAll()) {
|
for (const { attributes } of window.ConversationController.getAll()) {
|
||||||
const recipientId = this.getRecipientId({
|
const recipientId = this.getRecipientId({
|
||||||
id: attributes.id,
|
id: attributes.id,
|
||||||
|
@ -297,7 +307,11 @@ export class BackupExportStream extends Readable {
|
||||||
e164: attributes.e164,
|
e164: attributes.e164,
|
||||||
});
|
});
|
||||||
|
|
||||||
const recipient = this.toRecipient(recipientId, attributes);
|
const recipient = this.toRecipient(
|
||||||
|
recipientId,
|
||||||
|
attributes,
|
||||||
|
identityKeysById
|
||||||
|
);
|
||||||
if (recipient === undefined) {
|
if (recipient === undefined) {
|
||||||
// Can't be backed up.
|
// Can't be backed up.
|
||||||
continue;
|
continue;
|
||||||
|
@ -804,7 +818,8 @@ export class BackupExportStream extends Readable {
|
||||||
convo: Omit<
|
convo: Omit<
|
||||||
ConversationAttributesType,
|
ConversationAttributesType,
|
||||||
'id' | 'version' | 'expireTimerVersion'
|
'id' | 'version' | 'expireTimerVersion'
|
||||||
>
|
>,
|
||||||
|
identityKeysById?: ReadonlyMap<IdentityKeyType['id'], IdentityKeyType>
|
||||||
): Backups.IRecipient | undefined {
|
): Backups.IRecipient | undefined {
|
||||||
const res: Backups.IRecipient = {
|
const res: Backups.IRecipient = {
|
||||||
id: recipientId,
|
id: recipientId,
|
||||||
|
@ -824,6 +839,11 @@ export class BackupExportStream extends Readable {
|
||||||
throw missingCaseError(convo.removalStage);
|
throw missingCaseError(convo.removalStage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let identityKey: IdentityKeyType | undefined;
|
||||||
|
if (identityKeysById != null && convo.serviceId != null) {
|
||||||
|
identityKey = identityKeysById.get(convo.serviceId);
|
||||||
|
}
|
||||||
|
|
||||||
res.contact = {
|
res.contact = {
|
||||||
aci:
|
aci:
|
||||||
convo.serviceId && convo.serviceId !== convo.pni
|
convo.serviceId && convo.serviceId !== convo.pni
|
||||||
|
@ -856,6 +876,10 @@ export class BackupExportStream extends Readable {
|
||||||
profileGivenName: convo.profileName,
|
profileGivenName: convo.profileName,
|
||||||
profileFamilyName: convo.profileFamilyName,
|
profileFamilyName: convo.profileFamilyName,
|
||||||
hideStory: convo.hideStory === true,
|
hideStory: convo.hideStory === true,
|
||||||
|
identityKey: identityKey?.publicKey || null,
|
||||||
|
|
||||||
|
// Integer values match so we can use it as is
|
||||||
|
identityState: identityKey?.verified ?? 0,
|
||||||
};
|
};
|
||||||
} else if (isGroupV2(convo) && convo.masterKey) {
|
} else if (isGroupV2(convo) && convo.masterKey) {
|
||||||
let storySendMode: Backups.Group.StorySendMode;
|
let storySendMode: Backups.Group.StorySendMode;
|
||||||
|
@ -2092,6 +2116,15 @@ export class BackupExportStream extends Readable {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let quoteType: Backups.Quote.Type;
|
||||||
|
if (quote.isGiftBadge) {
|
||||||
|
quoteType = Backups.Quote.Type.GIFT_BADGE;
|
||||||
|
} else if (quote.isViewOnce) {
|
||||||
|
quoteType = Backups.Quote.Type.VIEW_ONCE;
|
||||||
|
} else {
|
||||||
|
quoteType = Backups.Quote.Type.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
targetSentTimestamp: Long.fromNumber(quote.id),
|
targetSentTimestamp: Long.fromNumber(quote.id),
|
||||||
authorId,
|
authorId,
|
||||||
|
@ -2123,9 +2156,7 @@ export class BackupExportStream extends Readable {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
type: quote.isGiftBadge
|
type: quoteType,
|
||||||
? Backups.Quote.Type.GIFTBADGE
|
|
||||||
: Backups.Quote.Type.NORMAL,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { DataReader, DataWriter } from '../../sql/Client';
|
||||||
import {
|
import {
|
||||||
AttachmentDownloadSource,
|
AttachmentDownloadSource,
|
||||||
type StoryDistributionWithMembersType,
|
type StoryDistributionWithMembersType,
|
||||||
|
type IdentityKeyType,
|
||||||
} from '../../sql/Interface';
|
} from '../../sql/Interface';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { GiftBadgeStates } from '../../components/conversation/Message';
|
import { GiftBadgeStates } from '../../components/conversation/Message';
|
||||||
|
@ -209,6 +210,7 @@ export class BackupImportStream extends Writable {
|
||||||
string,
|
string,
|
||||||
ConversationAttributesType
|
ConversationAttributesType
|
||||||
>();
|
>();
|
||||||
|
private readonly identityKeys = new Map<ServiceIdString, IdentityKeyType>();
|
||||||
private readonly saveMessageBatch = new Set<MessageAttributesType>();
|
private readonly saveMessageBatch = new Set<MessageAttributesType>();
|
||||||
private readonly stickerPacks = new Array<StickerPackPointerType>();
|
private readonly stickerPacks = new Array<StickerPackPointerType>();
|
||||||
private ourConversation?: ConversationAttributesType;
|
private ourConversation?: ConversationAttributesType;
|
||||||
|
@ -489,10 +491,14 @@ export class BackupImportStream extends Writable {
|
||||||
const saves = Array.from(this.conversations.values());
|
const saves = Array.from(this.conversations.values());
|
||||||
this.conversations.clear();
|
this.conversations.clear();
|
||||||
|
|
||||||
|
const identityKeys = Array.from(this.identityKeys.values());
|
||||||
|
this.identityKeys.clear();
|
||||||
|
|
||||||
// Queue writes at the same time to prevent races.
|
// Queue writes at the same time to prevent races.
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
DataWriter.saveConversations(saves),
|
DataWriter.saveConversations(saves),
|
||||||
DataWriter.updateConversations(updates),
|
DataWriter.updateConversations(updates),
|
||||||
|
DataWriter.bulkAddIdentityKeys(identityKeys),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -817,11 +823,13 @@ export class BackupImportStream extends Writable {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serviceId = aci ?? pni;
|
||||||
|
|
||||||
const attrs: ConversationAttributesType = {
|
const attrs: ConversationAttributesType = {
|
||||||
id: generateUuid(),
|
id: generateUuid(),
|
||||||
type: 'private',
|
type: 'private',
|
||||||
version: 2,
|
version: 2,
|
||||||
serviceId: aci ?? pni,
|
serviceId,
|
||||||
pni,
|
pni,
|
||||||
e164,
|
e164,
|
||||||
removalStage,
|
removalStage,
|
||||||
|
@ -836,6 +844,17 @@ export class BackupImportStream extends Writable {
|
||||||
expireTimerVersion: 1,
|
expireTimerVersion: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (serviceId != null && Bytes.isNotEmpty(contact.identityKey)) {
|
||||||
|
this.identityKeys.set(serviceId, {
|
||||||
|
id: serviceId,
|
||||||
|
publicKey: contact.identityKey,
|
||||||
|
verified: contact.identityState || 0,
|
||||||
|
firstUse: true,
|
||||||
|
timestamp: this.now,
|
||||||
|
nonblockingApproval: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (contact.notRegistered) {
|
if (contact.notRegistered) {
|
||||||
const timestamp = contact.notRegistered.unregisteredTimestamp?.toNumber();
|
const timestamp = contact.notRegistered.unregisteredTimestamp?.toNumber();
|
||||||
attrs.discoveredUnregisteredAt = timestamp || this.now;
|
attrs.discoveredUnregisteredAt = timestamp || this.now;
|
||||||
|
@ -848,7 +867,6 @@ export class BackupImportStream extends Writable {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contact.blocked) {
|
if (contact.blocked) {
|
||||||
const serviceId = aci || pni;
|
|
||||||
if (serviceId) {
|
if (serviceId) {
|
||||||
await window.storage.blocked.addBlockedServiceId(serviceId);
|
await window.storage.blocked.addBlockedServiceId(serviceId);
|
||||||
}
|
}
|
||||||
|
@ -1671,8 +1689,11 @@ export class BackupImportStream extends Writable {
|
||||||
type: Backups.Quote.Type | null | undefined
|
type: Backups.Quote.Type | null | undefined
|
||||||
): SignalService.DataMessage.Quote.Type {
|
): SignalService.DataMessage.Quote.Type {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Backups.Quote.Type.GIFTBADGE:
|
case Backups.Quote.Type.GIFT_BADGE:
|
||||||
return SignalService.DataMessage.Quote.Type.GIFT_BADGE;
|
return SignalService.DataMessage.Quote.Type.GIFT_BADGE;
|
||||||
|
case Backups.Quote.Type.VIEW_ONCE:
|
||||||
|
// No special treatment, we'll compute it once we find the message
|
||||||
|
return SignalService.DataMessage.Quote.Type.NORMAL;
|
||||||
case Backups.Quote.Type.NORMAL:
|
case Backups.Quote.Type.NORMAL:
|
||||||
case Backups.Quote.Type.UNKNOWN:
|
case Backups.Quote.Type.UNKNOWN:
|
||||||
case null:
|
case null:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue