1 package org
.asamk
.signal
.manager
.syncStorage
;
3 import org
.asamk
.signal
.manager
.groups
.GroupUtils
;
4 import org
.asamk
.signal
.manager
.storage
.SignalAccount
;
5 import org
.asamk
.signal
.manager
.util
.KeyUtils
;
6 import org
.signal
.libsignal
.zkgroup
.groups
.GroupMasterKey
;
7 import org
.slf4j
.Logger
;
8 import org
.slf4j
.LoggerFactory
;
9 import org
.whispersystems
.signalservice
.api
.storage
.SignalGroupV2Record
;
11 import java
.sql
.Connection
;
12 import java
.sql
.SQLException
;
13 import java
.util
.Arrays
;
14 import java
.util
.Optional
;
16 public final class GroupV2RecordProcessor
extends DefaultStorageRecordProcessor
<SignalGroupV2Record
> {
18 private static final Logger logger
= LoggerFactory
.getLogger(GroupV2RecordProcessor
.class);
19 private final SignalAccount account
;
20 private final Connection connection
;
22 public GroupV2RecordProcessor(SignalAccount account
, Connection connection
) {
23 this.account
= account
;
24 this.connection
= connection
;
28 protected boolean isInvalid(SignalGroupV2Record remote
) {
29 return remote
.getMasterKeyBytes().length
!= GroupMasterKey
.SIZE
;
33 protected Optional
<SignalGroupV2Record
> getMatching(SignalGroupV2Record remote
) throws SQLException
{
34 final var id
= GroupUtils
.getGroupIdV2(remote
.getMasterKeyOrThrow());
35 final var group
= account
.getGroupStore().getGroup(connection
, id
);
38 return Optional
.empty();
41 final var storageId
= account
.getGroupStore().getGroupStorageId(connection
, id
);
42 return Optional
.of(StorageSyncModels
.localToRemoteRecord(group
, storageId
.getRaw()).getGroupV2().get());
46 protected SignalGroupV2Record
merge(SignalGroupV2Record remote
, SignalGroupV2Record local
) {
47 final var unknownFields
= remote
.serializeUnknownFields();
48 final var blocked
= remote
.isBlocked();
49 final var profileSharing
= remote
.isProfileSharingEnabled();
50 final var archived
= remote
.isArchived();
51 final var forcedUnread
= remote
.isForcedUnread();
52 final var muteUntil
= remote
.getMuteUntil();
53 final var notifyForMentionsWhenMuted
= remote
.notifyForMentionsWhenMuted();
54 final var hideStory
= remote
.shouldHideStory();
55 final var storySendMode
= remote
.getStorySendMode();
57 final var mergedBuilder
= new SignalGroupV2Record
.Builder(remote
.getId().getRaw(),
58 remote
.getMasterKeyBytes(),
59 unknownFields
).setBlocked(blocked
)
60 .setProfileSharingEnabled(profileSharing
)
61 .setArchived(archived
)
62 .setForcedUnread(forcedUnread
)
63 .setMuteUntil(muteUntil
)
64 .setNotifyForMentionsWhenMuted(notifyForMentionsWhenMuted
)
65 .setHideStory(hideStory
)
66 .setStorySendMode(storySendMode
);
67 final var merged
= mergedBuilder
.build();
69 final var matchesRemote
= doProtosMatch(merged
, remote
);
74 final var matchesLocal
= doProtosMatch(merged
, local
);
79 return mergedBuilder
.setId(KeyUtils
.createRawStorageId()).build();
83 protected void insertLocal(SignalGroupV2Record
record) throws SQLException
{
84 StorageRecordUpdate
<SignalGroupV2Record
> update
= new StorageRecordUpdate
<>(null, record);
89 protected void updateLocal(StorageRecordUpdate
<SignalGroupV2Record
> update
) throws SQLException
{
90 final var groupV2Record
= update
.newRecord();
91 final var groupMasterKey
= groupV2Record
.getMasterKeyOrThrow();
93 final var group
= account
.getGroupStore().getGroupOrPartialMigrate(connection
, groupMasterKey
);
94 group
.setBlocked(groupV2Record
.isBlocked());
95 account
.getGroupStore().updateGroup(connection
, group
);
96 account
.getGroupStore()
97 .storeStorageRecord(connection
,
99 groupV2Record
.getId(),
100 groupV2Record
.toProto().encode());
104 public int compare(SignalGroupV2Record lhs
, SignalGroupV2Record rhs
) {
105 if (Arrays
.equals(lhs
.getMasterKeyBytes(), rhs
.getMasterKeyBytes())) {
112 private static boolean doProtosMatch(SignalGroupV2Record merged
, SignalGroupV2Record other
) {
113 return Arrays
.equals(merged
.toProto().encode(), other
.toProto().encode());