Export single contact, nickname

This commit is contained in:
Fedor Indutny 2025-01-08 16:14:04 -08:00 committed by GitHub
parent a877435eea
commit 8c57d243c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 256 additions and 166 deletions

View file

@ -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: 'a920df75ba02e011f6c56c59c6bb20571162a961' ref: 'afa66e77fe2b437024ec0914cd14c02c95e7cbed'
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

View file

@ -6044,7 +6044,7 @@ third-party/chromium/LICENSE.
``` ```
## windows-core 0.52.0, windows-sys 0.45.0, windows-sys 0.52.0, windows-sys 0.59.0, windows-targets 0.42.2, windows-targets 0.52.6, windows_aarch64_msvc 0.42.2, windows_aarch64_msvc 0.52.6, windows_x86_64_gnu 0.52.6, windows_x86_64_msvc 0.42.2, windows_x86_64_msvc 0.52.6 ## windows-core 0.52.0, windows-sys 0.45.0, windows-sys 0.52.0, windows-sys 0.59.0, windows-targets 0.42.2, windows-targets 0.48.5, windows-targets 0.52.6, windows_aarch64_msvc 0.42.2, windows_aarch64_msvc 0.48.5, windows_aarch64_msvc 0.52.6, windows_x86_64_gnu 0.48.5, windows_x86_64_gnu 0.52.6, windows_x86_64_msvc 0.42.2, windows_x86_64_msvc 0.48.5, windows_x86_64_msvc 0.52.6
``` ```
MIT License MIT License
@ -6254,7 +6254,7 @@ DEALINGS IN THE SOFTWARE.
``` ```
## idna 0.5.0, percent-encoding 2.3.1, url 2.5.2 ## idna 1.0.3, percent-encoding 2.3.1, url 2.5.4
``` ```
Copyright (c) 2013-2022 The rust-url developers Copyright (c) 2013-2022 The rust-url developers
@ -6834,7 +6834,7 @@ DEALINGS IN THE SOFTWARE.
``` ```
## gimli 0.31.0, heck 0.5.0, unicode-bidi 0.3.15, unicode-normalization 0.1.23 ## gimli 0.31.0, heck 0.5.0
``` ```
Copyright (c) 2015 The Rust Project Developers Copyright (c) 2015 The Rust Project Developers
@ -7181,7 +7181,7 @@ THE SOFTWARE.
``` ```
## futures-channel 0.3.30, futures-core 0.3.30, futures-executor 0.3.30, futures-io 0.3.30, futures-macro 0.3.30, futures-sink 0.3.30, futures-task 0.3.30, futures-util 0.3.30, futures 0.3.30 ## futures-channel 0.3.31, futures-core 0.3.31, futures-executor 0.3.30, futures-io 0.3.31, futures-macro 0.3.31, futures-sink 0.3.31, futures-task 0.3.31, futures-util 0.3.31, futures 0.3.30
``` ```
Copyright (c) 2016 Alex Crichton Copyright (c) 2016 Alex Crichton
@ -7275,7 +7275,7 @@ DEALINGS IN THE SOFTWARE.
``` ```
## intmap 2.0.0 ## intmap 3.0.0
``` ```
Copyright (c) 2016 Jesper Axelsson Copyright (c) 2016 Jesper Axelsson
@ -7349,7 +7349,7 @@ DEALINGS IN THE SOFTWARE.
``` ```
## rustls-native-certs 0.7.3, rustls-pemfile 2.1.3, rustls 0.22.4, rustls 0.23.13 ## rustls-native-certs 0.7.3, rustls-pemfile 2.1.3, rustls 0.22.4, rustls 0.23.20
``` ```
Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com> Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com>
@ -7945,6 +7945,36 @@ DEALINGS IN THE SOFTWARE.
``` ```
## stable_deref_trait 1.2.0
```
Copyright (c) 2017 Robert Grosse
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.
```
## foreign-types-macros 0.2.3, foreign-types-shared 0.3.1, foreign-types 0.5.0 ## foreign-types-macros 0.2.3, foreign-types-shared 0.3.1, foreign-types 0.5.0
``` ```
@ -9328,7 +9358,7 @@ SOFTWARE.
``` ```
## rustls-pki-types 1.8.0 ## rustls-pki-types 1.10.1
``` ```
Copyright (c) 2023 Dirkjan Ochtman <dirkjan@ochtman.nl> Copyright (c) 2023 Dirkjan Ochtman <dirkjan@ochtman.nl>
@ -9496,6 +9526,37 @@ The above copyright notice and this permission notice shall be included in all c
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. 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.
``` ```
## idna_adapter 1.2.0
```
Copyright (c) The rust-url developers
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.
```
## arrayvec 0.7.6 ## arrayvec 0.7.6
``` ```
@ -9527,6 +9588,19 @@ DEALINGS IN THE SOFTWARE.
``` ```
## synstructure 0.13.1
```
Copyright 2016 Nika Layzell
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.
```
## rand 0.8.5, rand_chacha 0.3.1, rand_core 0.6.4 ## rand 0.8.5, rand_chacha 0.3.1, rand_core 0.6.4
``` ```
@ -9604,7 +9678,7 @@ DEALINGS IN THE SOFTWARE.
``` ```
## encoding_rs 0.8.34 ## encoding_rs 0.8.34, utf16_iter 1.0.5, utf8_iter 1.0.4, write16 1.0.0
``` ```
Copyright Mozilla Foundation Copyright Mozilla Foundation
@ -10121,33 +10195,6 @@ SOFTWARE.
``` ```
## tinyvec_macros 0.1.1
```
MIT License
Copyright (c) 2020 Soveu
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.
```
## snow 0.9.6 ## snow 0.9.6
``` ```
@ -10229,7 +10276,7 @@ SOFTWARE.
``` ```
## rustls-platform-verifier-android 0.1.1, rustls-platform-verifier 0.3.4 ## rustls-platform-verifier-android 0.1.1, rustls-platform-verifier 0.4.0
``` ```
MIT License MIT License
@ -10430,17 +10477,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
``` ```
## tinyvec 1.8.0
```
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.
```
## android_system_properties 0.1.5 ## android_system_properties 0.1.5
``` ```
@ -11185,6 +11221,51 @@ SOFTWARE.
*/ */
``` ```
## icu_collections 1.5.0, icu_locid 1.5.0, icu_locid_transform 1.5.0, icu_locid_transform_data 1.5.0, icu_normalizer 1.5.0, icu_normalizer_data 1.5.0, icu_properties 1.5.1, icu_properties_data 1.5.0, icu_provider 1.5.0, icu_provider_macros 1.5.0, litemap 0.7.4, tinystr 0.7.6, writeable 0.5.5, yoke-derive 0.7.5, yoke 0.7.5, zerofrom-derive 0.1.5, zerofrom 0.1.5, zerovec-derive 0.10.3, zerovec 0.10.4
```
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 1991-2023 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.
THE DATA FILES AND SOFTWARE ARE 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 OF
THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.
```
## unicode-ident 1.0.13 ## unicode-ident 1.0.13
``` ```

9
package-lock.json generated
View file

@ -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.9", "@signalapp/better-sqlite3": "9.0.9",
"@signalapp/libsignal-client": "0.64.1", "@signalapp/libsignal-client": "0.65.0",
"@signalapp/ringrtc": "2.49.2", "@signalapp/ringrtc": "2.49.2",
"@types/fabric": "4.5.3", "@types/fabric": "4.5.3",
"backbone": "1.6.0", "backbone": "1.6.0",
@ -6468,10 +6468,11 @@
} }
}, },
"node_modules/@signalapp/libsignal-client": { "node_modules/@signalapp/libsignal-client": {
"version": "0.64.1", "version": "0.65.0",
"resolved": "https://registry.npmjs.org/@signalapp/libsignal-client/-/libsignal-client-0.64.1.tgz", "resolved": "https://registry.npmjs.org/@signalapp/libsignal-client/-/libsignal-client-0.65.0.tgz",
"integrity": "sha512-ex45KXdmPtTW9q4DkF/VM/K0XnuuXzsqFFkanEeUE07kEzjGfx1x9tVhQ5h2+lokVjEVLM1Oq6lVysCpk3iwcg==", "integrity": "sha512-6bb+454/ZoOu6EhHdKHSFVoNKRhka1FhqfiDL/FYLf79g+Nz4kXNxCMRJwYO3hzPAgjjs/6Q7S1mXr9CfS7Kkg==",
"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",

View file

@ -111,7 +111,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.9", "@signalapp/better-sqlite3": "9.0.9",
"@signalapp/libsignal-client": "0.64.1", "@signalapp/libsignal-client": "0.65.0",
"@signalapp/ringrtc": "2.49.2", "@signalapp/ringrtc": "2.49.2",
"@types/fabric": "4.5.3", "@types/fabric": "4.5.3",
"backbone": "1.6.0", "backbone": "1.6.0",

View file

@ -21,7 +21,7 @@ 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 // 4. ChatFolders must appear in render order (e.g., left to right for
// LTR locales), but can appear anywhere relative to other frames respecting // LTR locales), but can appear anywhere relative to other frames respecting
// rule 2 (after Recipients and Chats). // rule 2 (after Recipients and Chats).
// //
@ -137,7 +137,7 @@ message Contact {
UNVERIFIED = 2; UNVERIFIED = 2;
} }
message Registered { } message Registered {}
message NotRegistered { message NotRegistered {
uint64 unregisteredTimestamp = 1; uint64 unregisteredTimestamp = 1;
} }
@ -148,6 +148,11 @@ message Contact {
HIDDEN_MESSAGE_REQUEST = 2; HIDDEN_MESSAGE_REQUEST = 2;
} }
message Name {
string given = 1;
string family = 2;
}
optional bytes aci = 1; // should be 16 bytes optional bytes aci = 1; // should be 16 bytes
optional bytes pni = 2; // should be 16 bytes optional bytes pni = 2; // should be 16 bytes
optional string username = 3; optional string username = 3;
@ -167,6 +172,7 @@ message Contact {
bool hideStory = 13; bool hideStory = 13;
optional bytes identityKey = 14; optional bytes identityKey = 14;
IdentityState identityState = 15; IdentityState identityState = 15;
Name nickname = 16; // absent iff both `given` and `family` are empty
} }
message Group { message Group {
@ -206,7 +212,7 @@ message Group {
message GroupAttributeBlob { message GroupAttributeBlob {
oneof content { oneof content {
string title = 1; string title = 1;
bytes avatar = 2; bytes avatar = 2;
uint32 disappearingMessagesDuration = 3; uint32 disappearingMessagesDuration = 3;
string descriptionText = 4; string descriptionText = 4;
} }
@ -438,7 +444,7 @@ message StandardMessage {
} }
message ContactMessage { message ContactMessage {
repeated ContactAttachment contact = 1; ContactAttachment contact = 1;
repeated Reaction reactions = 2; repeated Reaction reactions = 2;
} }
@ -467,14 +473,14 @@ message PaymentNotification {
Status status = 1; Status status = 1;
// This identification is used to map the payment table to the ledger // This identification is used to map the payment table to the ledger
// and is likely required otherwise we may have issues reconciling with // and is likely required otherwise we may have issues reconciling with
// the ledger // the ledger
MobileCoinTxoIdentification mobileCoinIdentification = 2; MobileCoinTxoIdentification mobileCoinIdentification = 2;
optional uint64 timestamp = 3; optional uint64 timestamp = 3;
optional uint64 blockIndex = 4; optional uint64 blockIndex = 4;
optional uint64 blockTimestamp = 5; optional uint64 blockTimestamp = 5;
optional bytes transaction = 6; // mobile coin blobs optional bytes transaction = 6; // mobile coin blobs
optional bytes receipt = 7; // mobile coin blobs optional bytes receipt = 7; // mobile coin blobs
} }
oneof payment { oneof payment {
@ -482,12 +488,11 @@ message PaymentNotification {
FailedTransaction failedTransaction = 2; FailedTransaction failedTransaction = 2;
} }
} }
optional string amountMob = 1; // stored as a decimal string, e.g. 1.00001 optional string amountMob = 1; // stored as a decimal string, e.g. 1.00001
optional string feeMob = 2; // stored as a decimal string, e.g. 1.00001 optional string feeMob = 2; // stored as a decimal string, e.g. 1.00001
optional string note = 3; optional string note = 3;
TransactionDetails transactionDetails = 4; TransactionDetails transactionDetails = 4;
} }
message GiftBadge { message GiftBadge {
@ -510,12 +515,12 @@ message ViewOnceMessage {
message ContactAttachment { message ContactAttachment {
message Name { message Name {
optional string givenName = 1; string givenName = 1;
optional string familyName = 2; string familyName = 2;
optional string prefix = 3; string prefix = 3;
optional string suffix = 4; string suffix = 4;
optional string middleName = 5; string middleName = 5;
optional string nickname = 6; string nickname = 6;
} }
message Phone { message Phone {
@ -527,9 +532,9 @@ message ContactAttachment {
CUSTOM = 4; CUSTOM = 4;
} }
optional string value = 1; string value = 1;
optional Type type = 2; Type type = 2;
optional string label = 3; string label = 3;
} }
message Email { message Email {
@ -541,9 +546,9 @@ message ContactAttachment {
CUSTOM = 4; CUSTOM = 4;
} }
optional string value = 1; string value = 1;
optional Type type = 2; Type type = 2;
optional string label = 3; string label = 3;
} }
message PostalAddress { message PostalAddress {
@ -554,15 +559,15 @@ message ContactAttachment {
CUSTOM = 3; CUSTOM = 3;
} }
optional Type type = 1; Type type = 1;
optional string label = 2; string label = 2;
optional string street = 3; string street = 3;
optional string pobox = 4; string pobox = 4;
optional string neighborhood = 5; string neighborhood = 5;
optional string city = 6; string city = 6;
optional string region = 7; string region = 7;
optional string postcode = 8; string postcode = 8;
optional string country = 9; string country = 9;
} }
optional Name name = 1; optional Name name = 1;
@ -570,7 +575,7 @@ message ContactAttachment {
repeated Email email = 4; repeated Email email = 4;
repeated PostalAddress address = 5; repeated PostalAddress address = 5;
optional FilePointer avatar = 6; optional FilePointer avatar = 6;
optional string organization = 7; string organization = 7;
} }
message StickerMessage { message StickerMessage {
@ -652,7 +657,7 @@ message FilePointer {
} }
// References attachments that are invalid in such a way where download // References attachments that are invalid in such a way where download
// cannot be attempted. Could range from missing digests to missing // cannot be attempted. Could range from missing digests to missing
// CDN keys or anything else that makes download attempts impossible. // CDN keys or anything else that makes download attempts impossible.
// This serves as a 'tombstone' so that the UX can show that an attachment // This serves as a 'tombstone' so that the UX can show that an attachment
// did exist, but for whatever reason it's not retrievable. // did exist, but for whatever reason it's not retrievable.
@ -661,7 +666,7 @@ message FilePointer {
oneof locator { oneof locator {
BackupLocator backupLocator = 1; BackupLocator backupLocator = 1;
AttachmentLocator attachmentLocator= 2; AttachmentLocator attachmentLocator = 2;
InvalidAttachmentLocator invalidAttachmentLocator = 3; InvalidAttachmentLocator invalidAttachmentLocator = 3;
} }
@ -798,7 +803,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;
optional uint64 endedCallTimestamp = 6; // The time the call ended. optional uint64 endedCallTimestamp = 6; // The time the call ended.
bool read = 7; bool read = 7;
} }
@ -1072,7 +1077,7 @@ message GroupMemberJoinedByLinkUpdate {
} }
// A gv1->gv2 migration occurred. // A gv1->gv2 migration occurred.
message GroupV2MigrationUpdate {} message GroupV2MigrationUpdate {}
// Another user migrated gv1->gv2 but was unable to add // Another user migrated gv1->gv2 but was unable to add
// the local user and invited them instead. // the local user and invited them instead.
@ -1236,4 +1241,4 @@ message ChatFolder {
FolderType folderType = 6; FolderType folderType = 6;
repeated uint64 includedRecipientIds = 7; // generated recipient id of groups, contacts, and/or note to self 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 repeated uint64 excludedRecipientIds = 8; // generated recipient id of groups, contacts, and/or note to self
} }

View file

@ -212,7 +212,7 @@ export class BackupExportStream extends Readable {
private readonly roomIdToRecipientId = new Map<string, number>(); private readonly roomIdToRecipientId = new Map<string, number>();
private attachmentBackupJobs: Array<CoreAttachmentBackupJobType> = []; private attachmentBackupJobs: Array<CoreAttachmentBackupJobType> = [];
private buffers = new Array<Uint8Array>(); private buffers = new Array<Uint8Array>();
private nextRecipientId = 0; private nextRecipientId = 1;
private flushResolve: (() => void) | undefined; private flushResolve: (() => void) | undefined;
// Map from custom color uuid to an index in accountSettings.customColors // Map from custom color uuid to an index in accountSettings.customColors
@ -836,6 +836,8 @@ export class BackupExportStream extends Readable {
identityKey = identityKeysById.get(convo.serviceId); identityKey = identityKeysById.get(convo.serviceId);
} }
const { nicknameGivenName, nicknameFamilyName } = convo;
res.contact = { res.contact = {
aci: aci:
convo.serviceId && convo.serviceId !== convo.pni convo.serviceId && convo.serviceId !== convo.pni
@ -872,6 +874,13 @@ export class BackupExportStream extends Readable {
// Integer values match so we can use it as is // Integer values match so we can use it as is
identityState: identityKey?.verified ?? 0, identityState: identityKey?.verified ?? 0,
nickname:
nicknameGivenName || nicknameFamilyName
? {
given: nicknameGivenName,
family: nicknameFamilyName,
}
: null,
}; };
} else if (isGroupV2(convo) && convo.masterKey) { } else if (isGroupV2(convo) && convo.masterKey) {
let storySendMode: Backups.Group.StorySendMode; let storySendMode: Backups.Group.StorySendMode;
@ -1123,32 +1132,31 @@ export class BackupExportStream extends Readable {
throw missingCaseError(payment); throw missingCaseError(payment);
} }
} else if (contact && contact[0]) { } else if (contact && contact[0]) {
const [contactDetails] = contact;
const contactMessage = new Backups.ContactMessage(); const contactMessage = new Backups.ContactMessage();
contactMessage.contact = await Promise.all( contactMessage.contact = {
contact.map(async contactDetails => ({ ...contactDetails,
...contactDetails, number: contactDetails.number?.map(number => ({
number: contactDetails.number?.map(number => ({ ...number,
...number, type: numberToPhoneType(number.type),
type: numberToPhoneType(number.type), })),
})), email: contactDetails.email?.map(email => ({
email: contactDetails.email?.map(email => ({ ...email,
...email, type: numberToPhoneType(email.type),
type: numberToPhoneType(email.type), })),
})), address: contactDetails.address?.map(address => ({
address: contactDetails.address?.map(address => ({ ...address,
...address, type: numberToAddressType(address.type),
type: numberToAddressType(address.type), })),
})), avatar: contactDetails.avatar?.avatar
avatar: contactDetails.avatar?.avatar ? await this.processAttachment({
? await this.processAttachment({ attachment: contactDetails.avatar.avatar,
attachment: contactDetails.avatar.avatar, backupLevel,
backupLevel, messageReceivedAt: message.received_at,
messageReceivedAt: message.received_at, })
}) : undefined,
: undefined, };
}))
);
const reactions = this.getMessageReactions(message); const reactions = this.getMessageReactions(message);
if (reactions != null) { if (reactions != null) {

View file

@ -895,6 +895,8 @@ export class BackupImportStream extends Writable {
hideStory: contact.hideStory === true, hideStory: contact.hideStory === true,
username: dropNull(contact.username), username: dropNull(contact.username),
expireTimerVersion: 1, expireTimerVersion: 1,
nicknameGivenName: dropNull(contact.nickname?.given),
nicknameFamilyName: dropNull(contact.nickname?.family),
}; };
if (serviceId != null && Bytes.isNotEmpty(contact.identityKey)) { if (serviceId != null && Bytes.isNotEmpty(contact.identityKey)) {
@ -1908,21 +1910,23 @@ export class BackupImportStream extends Writable {
throw new Error(`${logId}: Got chat item with standardMessage set!`); throw new Error(`${logId}: Got chat item with standardMessage set!`);
} }
if (chatItem.contactMessage) { if (chatItem.contactMessage) {
const { contact: details } = chatItem.contactMessage;
strictAssert(details != null, 'contactMessage must have a contact');
const { avatar, name, number, email, address, organization } = details;
return { return {
message: { message: {
contact: (chatItem.contactMessage.contact ?? []).map(details => { contact: [
const { avatar, name, number, email, address, organization } = {
details;
return {
name: name name: name
? { ? {
givenName: dropNull(name.givenName), givenName: name.givenName || undefined,
familyName: dropNull(name.familyName), familyName: name.familyName || undefined,
prefix: dropNull(name.prefix), prefix: name.prefix || undefined,
suffix: dropNull(name.suffix), suffix: name.suffix || undefined,
middleName: dropNull(name.middleName), middleName: name.middleName || undefined,
nickname: dropNull(name.nickname), nickname: name.nickname || undefined,
} }
: undefined, : undefined,
number: number?.length number: number?.length
@ -1935,7 +1939,7 @@ export class BackupImportStream extends Writable {
return { return {
value, value,
type: phoneToContactFormType(type), type: phoneToContactFormType(type),
label: dropNull(label), label: label || undefined,
}; };
}) })
.filter(isNotNil) .filter(isNotNil)
@ -1950,7 +1954,7 @@ export class BackupImportStream extends Writable {
return { return {
value, value,
type: emailToContactFormType(type), type: emailToContactFormType(type),
label: dropNull(label), label: label || undefined,
}; };
}) })
.filter(isNotNil) .filter(isNotNil)
@ -1971,26 +1975,26 @@ export class BackupImportStream extends Writable {
return { return {
type: addressToContactAddressType(type), type: addressToContactAddressType(type),
label: dropNull(label), label: label || undefined,
street: dropNull(street), street: street || undefined,
pobox: dropNull(pobox), pobox: pobox || undefined,
neighborhood: dropNull(neighborhood), neighborhood: neighborhood || undefined,
city: dropNull(city), city: city || undefined,
region: dropNull(region), region: region || undefined,
postcode: dropNull(postcode), postcode: postcode || undefined,
country: dropNull(country), country: country || undefined,
}; };
}) })
: undefined, : undefined,
organization: dropNull(organization), organization: organization || undefined,
avatar: avatar avatar: avatar
? { ? {
avatar: convertFilePointerToAttachment(avatar), avatar: convertFilePointerToAttachment(avatar),
isProfile: false, isProfile: false,
} }
: undefined, : undefined,
}; },
}), ],
reactions: this.fromReactions(chatItem.contactMessage.reactions), reactions: this.fromReactions(chatItem.contactMessage.reactions),
}, },
additionalMessages: [], additionalMessages: [],

View file

@ -364,12 +364,14 @@ describe('backup/attachments', () => {
await asymmetricRoundtripHarness( await asymmetricRoundtripHarness(
[ [
composeMessage(1, { composeMessage(1, {
body: 'url',
preview: [{ url: 'url', date: 1, image: attachment }], preview: [{ url: 'url', date: 1, image: attachment }],
}), }),
], ],
// path & iv will not be roundtripped // path & iv will not be roundtripped
[ [
composeMessage(1, { composeMessage(1, {
body: 'url',
preview: [ preview: [
{ {
url: 'url', url: 'url',
@ -389,6 +391,7 @@ describe('backup/attachments', () => {
await asymmetricRoundtripHarness( await asymmetricRoundtripHarness(
[ [
composeMessage(1, { composeMessage(1, {
body: 'url',
preview: [ preview: [
{ {
url: 'url', url: 'url',
@ -402,6 +405,7 @@ describe('backup/attachments', () => {
], ],
[ [
composeMessage(1, { composeMessage(1, {
body: 'url',
preview: [ preview: [
{ {
url: 'url', url: 'url',
@ -501,12 +505,14 @@ describe('backup/attachments', () => {
await asymmetricRoundtripHarness( await asymmetricRoundtripHarness(
[ [
composeMessage(1, { composeMessage(1, {
body: '123',
quote: quotedMessage, quote: quotedMessage,
}), }),
], ],
// path & iv will not be roundtripped // path & iv will not be roundtripped
[ [
composeMessage(1, { composeMessage(1, {
body: '123',
quote: { quote: {
...quotedMessage, ...quotedMessage,
attachments: [ attachments: [
@ -537,11 +543,13 @@ describe('backup/attachments', () => {
await asymmetricRoundtripHarness( await asymmetricRoundtripHarness(
[ [
composeMessage(1, { composeMessage(1, {
body: '123',
quote: quotedMessage, quote: quotedMessage,
}), }),
], ],
[ [
composeMessage(1, { composeMessage(1, {
body: '123',
quote: { quote: {
...quotedMessage, ...quotedMessage,
attachments: [ attachments: [
@ -566,6 +574,7 @@ describe('backup/attachments', () => {
const existingAttachment = composeAttachment(1); const existingAttachment = composeAttachment(1);
const existingMessageTimestamp = Date.now(); const existingMessageTimestamp = Date.now();
const existingMessage = composeMessage(existingMessageTimestamp, { const existingMessage = composeMessage(existingMessageTimestamp, {
body: '123',
attachments: [existingAttachment], attachments: [existingAttachment],
}); });
@ -584,6 +593,7 @@ describe('backup/attachments', () => {
}; };
const quoteMessage = composeMessage(existingMessageTimestamp + 1, { const quoteMessage = composeMessage(existingMessageTimestamp + 1, {
body: 'quote',
quote: quotedMessage, quote: quotedMessage,
}); });
@ -635,7 +645,9 @@ describe('backup/attachments', () => {
}); });
it('handles quotes which have been copied over from the original (and lack all encryption info)', async () => { it('handles quotes which have been copied over from the original (and lack all encryption info)', async () => {
const originalMessage = composeMessage(1); const originalMessage = composeMessage(1, {
body: 'original',
});
const quotedMessage: QuotedMessageType = { const quotedMessage: QuotedMessageType = {
authorAci: originalMessage.sourceServiceId as AciString, authorAci: originalMessage.sourceServiceId as AciString,
isViewOnce: false, isViewOnce: false,
@ -655,6 +667,7 @@ describe('backup/attachments', () => {
}; };
const quoteMessage = composeMessage(originalMessage.timestamp + 1, { const quoteMessage = composeMessage(originalMessage.timestamp + 1, {
body: 'quote',
quote: quotedMessage, quote: quotedMessage,
}); });

View file

@ -264,6 +264,7 @@ describe('backup/bubble messages', () => {
seenStatus: SeenStatus.Unseen, seenStatus: SeenStatus.Unseen,
unidentifiedDeliveryReceived: true, unidentifiedDeliveryReceived: true,
timestamp: 4, timestamp: 4,
body: '123',
quote: { quote: {
authorAci: CONTACT_A, authorAci: CONTACT_A,
attachments: [], attachments: [],
@ -289,6 +290,7 @@ describe('backup/bubble messages', () => {
seenStatus: SeenStatus.Unseen, seenStatus: SeenStatus.Unseen,
unidentifiedDeliveryReceived: true, unidentifiedDeliveryReceived: true,
timestamp: 3, timestamp: 3,
body: '123',
quote: { quote: {
authorAci: CONTACT_A, authorAci: CONTACT_A,
attachments: [], attachments: [],
@ -314,6 +316,7 @@ describe('backup/bubble messages', () => {
seenStatus: SeenStatus.Unseen, seenStatus: SeenStatus.Unseen,
unidentifiedDeliveryReceived: true, unidentifiedDeliveryReceived: true,
timestamp: 3, timestamp: 3,
body: '123',
quote: { quote: {
authorAci: CONTACT_A, authorAci: CONTACT_A,
attachments: [], attachments: [],
@ -353,6 +356,7 @@ describe('backup/bubble messages', () => {
seenStatus: SeenStatus.Unseen, seenStatus: SeenStatus.Unseen,
unidentifiedDeliveryReceived: true, unidentifiedDeliveryReceived: true,
timestamp: 3, timestamp: 3,
body: '123',
quote: { quote: {
authorAci: CONTACT_A, authorAci: CONTACT_A,
attachments: [], attachments: [],

View file

@ -652,21 +652,5 @@ describe('Contact', () => {
const result = _validate(contact, { messageId }); const result = _validate(contact, { messageId });
assert.deepEqual(result?.message, expected); assert.deepEqual(result?.message, expected);
}); });
it('logs if no values remain in contact', async () => {
const messageId = 'the-message-id';
const contact = {
name: {
nickname: 'Someone Somewhere',
},
number: [],
email: [],
};
const expected =
'Message the-message-id: Contact had no included numbers, email or addresses';
const result = _validate(contact, { messageId });
assert.deepEqual(result?.message, expected);
});
}); });
}); });

View file

@ -306,7 +306,7 @@ export function _validate(
contact: EmbeddedContactType, contact: EmbeddedContactType,
{ messageId }: { messageId: string } { messageId }: { messageId: string }
): Error | undefined { ): Error | undefined {
const { number, email, address, organization } = contact; const { organization } = contact;
if (!getDisplayName(contact) && !organization) { if (!getDisplayName(contact) && !organization) {
return new Error( return new Error(
@ -314,16 +314,6 @@ export function _validate(
); );
} }
if (
(!number || !number.length) &&
(!email || !email.length) &&
(!address || !address.length)
) {
return new Error(
`Message ${messageId}: Contact had no included numbers, email or addresses`
);
}
return undefined; return undefined;
} }