]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/syncStorage/GroupV2RecordProcessor.java
af77e857cf356da1cd05b182ddfcb6f74574b342
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / syncStorage / GroupV2RecordProcessor.java
1 package org.asamk.signal.manager.syncStorage;
2
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.jetbrains.annotations.NotNull;
7 import org.signal.libsignal.zkgroup.InvalidInputException;
8 import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.whispersystems.signalservice.api.storage.SignalGroupV2Record;
12 import org.whispersystems.signalservice.api.storage.StorageId;
13 import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record;
14
15 import java.sql.Connection;
16 import java.sql.SQLException;
17 import java.util.Arrays;
18 import java.util.Optional;
19
20 import okio.ByteString;
21
22 public final class GroupV2RecordProcessor extends DefaultStorageRecordProcessor<SignalGroupV2Record> {
23
24 private static final Logger logger = LoggerFactory.getLogger(GroupV2RecordProcessor.class);
25 private final SignalAccount account;
26 private final Connection connection;
27
28 public GroupV2RecordProcessor(SignalAccount account, Connection connection) {
29 this.account = account;
30 this.connection = connection;
31 }
32
33 @Override
34 protected boolean isInvalid(SignalGroupV2Record remote) {
35 return remote.getProto().masterKey.size() != GroupMasterKey.SIZE;
36 }
37
38 @Override
39 protected Optional<SignalGroupV2Record> getMatching(SignalGroupV2Record remote) throws SQLException {
40 final var id = GroupUtils.getGroupIdV2(getGroupMasterKeyOrThrow(remote.getProto().masterKey));
41 final var group = account.getGroupStore().getGroup(connection, id);
42
43 if (group == null) {
44 return Optional.empty();
45 }
46
47 final var storageId = account.getGroupStore().getGroupStorageId(connection, id);
48 return Optional.of(new SignalGroupV2Record(storageId, StorageSyncModels.localToRemoteRecord(group)));
49 }
50
51 @Override
52 protected SignalGroupV2Record merge(SignalGroupV2Record remoteRecord, SignalGroupV2Record localRecord) {
53 final var remote = remoteRecord.getProto();
54 final var local = localRecord.getProto();
55
56 final var mergedBuilder = SignalGroupV2Record.Companion.newBuilder(remote.unknownFields().toByteArray())
57 .masterKey(remote.masterKey)
58 .blocked(remote.blocked)
59 .whitelisted(remote.whitelisted)
60 .archived(remote.archived)
61 .markedUnread(remote.markedUnread)
62 .mutedUntilTimestamp(remote.mutedUntilTimestamp)
63 .dontNotifyForMentionsIfMuted(remote.dontNotifyForMentionsIfMuted)
64 .hideStory(remote.hideStory)
65 .storySendMode(remote.storySendMode);
66 final var merged = mergedBuilder.build();
67
68 final var matchesRemote = doProtosMatch(merged, remote);
69 if (matchesRemote) {
70 return remoteRecord;
71 }
72
73 final var matchesLocal = doProtosMatch(merged, local);
74 if (matchesLocal) {
75 return localRecord;
76 }
77
78 return new SignalGroupV2Record(StorageId.forGroupV2(KeyUtils.createRawStorageId()), mergedBuilder.build());
79 }
80
81 @Override
82 protected void insertLocal(SignalGroupV2Record record) throws SQLException {
83 StorageRecordUpdate<SignalGroupV2Record> update = new StorageRecordUpdate<>(null, record);
84 updateLocal(update);
85 }
86
87 @Override
88 protected void updateLocal(StorageRecordUpdate<SignalGroupV2Record> update) throws SQLException {
89 final var groupV2Record = update.newRecord();
90 final var groupV2Proto = groupV2Record.getProto();
91 final var groupMasterKey = getGroupMasterKeyOrThrow(groupV2Proto.masterKey);
92
93 final var group = account.getGroupStore().getGroupOrPartialMigrate(connection, groupMasterKey);
94 group.setBlocked(groupV2Proto.blocked);
95 group.setProfileSharingEnabled(groupV2Proto.whitelisted);
96 account.getGroupStore().updateGroup(connection, group);
97 account.getGroupStore()
98 .storeStorageRecord(connection, group.getGroupId(), groupV2Record.getId(), groupV2Proto.encode());
99 }
100
101 @NotNull
102 private static GroupMasterKey getGroupMasterKeyOrThrow(final ByteString masterKey) {
103 try {
104 return new GroupMasterKey(masterKey.toByteArray());
105 } catch (InvalidInputException e) {
106 throw new AssertionError(e);
107 }
108 }
109
110 @Override
111 public int compare(SignalGroupV2Record lhs, SignalGroupV2Record rhs) {
112 if (lhs.getProto().masterKey.equals(rhs.getProto().masterKey)) {
113 return 0;
114 } else {
115 return 1;
116 }
117 }
118
119 private static boolean doProtosMatch(GroupV2Record merged, GroupV2Record other) {
120 return Arrays.equals(merged.encode(), other.encode());
121 }
122 }