1 package org
.asamk
.signal
.manager
.syncStorage
;
3 import org
.asamk
.signal
.manager
.api
.PhoneNumberSharingMode
;
4 import org
.asamk
.signal
.manager
.api
.TrustLevel
;
5 import org
.asamk
.signal
.manager
.storage
.configuration
.ConfigurationStore
;
6 import org
.asamk
.signal
.manager
.storage
.groups
.GroupInfoV1
;
7 import org
.asamk
.signal
.manager
.storage
.groups
.GroupInfoV2
;
8 import org
.asamk
.signal
.manager
.storage
.identities
.IdentityInfo
;
9 import org
.asamk
.signal
.manager
.storage
.recipients
.Recipient
;
10 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
.ACI
;
11 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
.PNI
;
12 import org
.whispersystems
.signalservice
.api
.push
.UsernameLinkComponents
;
13 import org
.whispersystems
.signalservice
.api
.storage
.SignalAccountRecord
;
14 import org
.whispersystems
.signalservice
.api
.storage
.SignalContactRecord
;
15 import org
.whispersystems
.signalservice
.api
.storage
.SignalGroupV1Record
;
16 import org
.whispersystems
.signalservice
.api
.storage
.SignalGroupV2Record
;
17 import org
.whispersystems
.signalservice
.api
.util
.UuidUtil
;
18 import org
.whispersystems
.signalservice
.internal
.storage
.protos
.AccountRecord
;
19 import org
.whispersystems
.signalservice
.internal
.storage
.protos
.AccountRecord
.UsernameLink
;
20 import org
.whispersystems
.signalservice
.internal
.storage
.protos
.ContactRecord
;
21 import org
.whispersystems
.signalservice
.internal
.storage
.protos
.ContactRecord
.IdentityState
;
22 import org
.whispersystems
.signalservice
.internal
.storage
.protos
.GroupV1Record
;
23 import org
.whispersystems
.signalservice
.internal
.storage
.protos
.GroupV2Record
;
25 import java
.sql
.Connection
;
26 import java
.sql
.SQLException
;
27 import java
.util
.Optional
;
29 import okio
.ByteString
;
31 import static org
.signal
.core
.util
.StringExtensionsKt
.emptyIfNull
;
33 public final class StorageSyncModels
{
35 private StorageSyncModels() {
38 public static AccountRecord
.PhoneNumberSharingMode
localToRemote(PhoneNumberSharingMode phoneNumberPhoneNumberSharingMode
) {
39 return switch (phoneNumberPhoneNumberSharingMode
) {
40 case EVERYBODY
-> AccountRecord
.PhoneNumberSharingMode
.EVERYBODY
;
41 case CONTACTS
, NOBODY
-> AccountRecord
.PhoneNumberSharingMode
.NOBODY
;
45 public static PhoneNumberSharingMode
remoteToLocal(AccountRecord
.PhoneNumberSharingMode phoneNumberPhoneNumberSharingMode
) {
46 return switch (phoneNumberPhoneNumberSharingMode
) {
47 case EVERYBODY
-> PhoneNumberSharingMode
.EVERYBODY
;
48 case NOBODY
-> PhoneNumberSharingMode
.NOBODY
;
53 public static AccountRecord
localToRemoteRecord(
54 final Connection connection
,
55 ConfigurationStore configStore
,
57 UsernameLinkComponents usernameLinkComponents
58 ) throws SQLException
{
59 final var builder
= SignalAccountRecord
.Companion
.newBuilder(self
.getStorageRecord());
60 if (self
.getProfileKey() != null) {
61 builder
.profileKey(ByteString
.of(self
.getProfileKey().serialize()));
63 if (self
.getProfile() != null) {
64 builder
.givenName(emptyIfNull(self
.getProfile().getGivenName()))
65 .familyName(emptyIfNull(self
.getProfile().getFamilyName()))
66 .avatarUrlPath(emptyIfNull(self
.getProfile().getAvatarUrlPath()));
68 builder
.typingIndicators(Optional
.ofNullable(configStore
.getTypingIndicators(connection
)).orElse(true))
69 .readReceipts(Optional
.ofNullable(configStore
.getReadReceipts(connection
)).orElse(true))
70 .sealedSenderIndicators(Optional
.ofNullable(configStore
.getUnidentifiedDeliveryIndicators(connection
))
72 .linkPreviews(Optional
.ofNullable(configStore
.getLinkPreviews(connection
)).orElse(true))
73 .unlistedPhoneNumber(Optional
.ofNullable(configStore
.getPhoneNumberUnlisted(connection
)).orElse(false))
74 .phoneNumberSharingMode(Optional
.ofNullable(configStore
.getPhoneNumberSharingMode(connection
))
75 .map(StorageSyncModels
::localToRemote
)
76 .orElse(AccountRecord
.PhoneNumberSharingMode
.UNKNOWN
))
77 .e164(self
.getAddress().number().orElse(""))
78 .username(self
.getAddress().username().orElse(""));
79 if (usernameLinkComponents
!= null) {
80 final var linkColor
= configStore
.getUsernameLinkColor(connection
);
81 builder
.usernameLink(new UsernameLink
.Builder().entropy(ByteString
.of(usernameLinkComponents
.getEntropy()))
82 .serverId(UuidUtil
.toByteString(usernameLinkComponents
.getServerId()))
83 .color(linkColor
== null ? UsernameLink
.Color
.UNKNOWN
: UsernameLink
.Color
.valueOf(linkColor
))
87 return builder
.build();
90 public static ContactRecord
localToRemoteRecord(Recipient recipient
, IdentityInfo identity
) {
91 final var address
= recipient
.getAddress();
92 final var builder
= SignalContactRecord
.Companion
.newBuilder(recipient
.getStorageRecord())
93 .aci(address
.aci().map(ACI
::toString
).orElse(""))
94 .e164(address
.number().orElse(""))
95 .pni(address
.pni().map(PNI
::toStringWithoutPrefix
).orElse(""))
96 .username(address
.username().orElse(""))
97 .profileKey(recipient
.getProfileKey() == null
99 : ByteString
.of(recipient
.getProfileKey().serialize()));
100 if (recipient
.getProfile() != null) {
101 builder
.givenName(emptyIfNull(recipient
.getProfile().getGivenName()))
102 .familyName(emptyIfNull(recipient
.getProfile().getFamilyName()));
104 if (recipient
.getContact() != null) {
105 builder
.systemGivenName(emptyIfNull(recipient
.getContact().givenName()))
106 .systemFamilyName(emptyIfNull(recipient
.getContact().familyName()))
107 .systemNickname(emptyIfNull(recipient
.getContact().nickName()))
108 .nickname(new ContactRecord
.Name
.Builder().given(emptyIfNull(recipient
.getContact()
109 .nickNameGivenName()))
110 .family(emptyIfNull(recipient
.getContact().nickNameFamilyName()))
112 .note(emptyIfNull(recipient
.getContact().note()))
113 .blocked(recipient
.getContact().isBlocked())
114 .whitelisted(recipient
.getContact().isProfileSharingEnabled())
115 .mutedUntilTimestamp(recipient
.getContact().muteUntil())
116 .hideStory(recipient
.getContact().hideStory())
117 .unregisteredAtTimestamp(recipient
.getContact().unregisteredTimestamp() == null
119 : recipient
.getContact().unregisteredTimestamp())
120 .archived(recipient
.getContact().isArchived())
121 .hidden(recipient
.getContact().isHidden());
123 if (identity
!= null) {
124 builder
.identityKey(ByteString
.of(identity
.getIdentityKey().serialize()))
125 .identityState(localToRemote(identity
.getTrustLevel()));
127 return builder
.build();
130 public static GroupV1Record
localToRemoteRecord(GroupInfoV1 group
) {
131 final var builder
= SignalGroupV1Record
.Companion
.newBuilder(group
.getStorageRecord());
132 builder
.id(ByteString
.of(group
.getGroupId().serialize()));
133 builder
.blocked(group
.isBlocked());
134 builder
.archived(group
.archived
);
135 builder
.whitelisted(true);
136 return builder
.build();
139 public static GroupV2Record
localToRemoteRecord(GroupInfoV2 group
) {
140 final var builder
= SignalGroupV2Record
.Companion
.newBuilder(group
.getStorageRecord());
141 builder
.masterKey(ByteString
.of(group
.getMasterKey().serialize()));
142 builder
.blocked(group
.isBlocked());
143 builder
.whitelisted(group
.isProfileSharingEnabled());
144 return builder
.build();
147 public static TrustLevel
remoteToLocal(IdentityState identityState
) {
148 return switch (identityState
) {
149 case DEFAULT
-> TrustLevel
.TRUSTED_UNVERIFIED
;
150 case UNVERIFIED
-> TrustLevel
.UNTRUSTED
;
151 case VERIFIED
-> TrustLevel
.TRUSTED_VERIFIED
;
155 private static IdentityState
localToRemote(TrustLevel local
) {
156 return switch (local
) {
157 case TRUSTED_VERIFIED
-> IdentityState
.VERIFIED
;
158 case UNTRUSTED
-> IdentityState
.UNVERIFIED
;
159 default -> IdentityState
.DEFAULT
;