]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/syncStorage/GroupV2RecordProcessor.java
3c014463bd1712d821f4681ea6a3058b3c6f8a2b
[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 .avatarColor(remote.avatarColor);
67 final var merged = mergedBuilder.build();
68
69 final var matchesRemote = doProtosMatch(merged, remote);
70 if (matchesRemote) {
71 return remoteRecord;
72 }
73
74 final var matchesLocal = doProtosMatch(merged, local);
75 if (matchesLocal) {
76 return localRecord;
77 }
78
79 return new SignalGroupV2Record(StorageId.forGroupV2(KeyUtils.createRawStorageId()), mergedBuilder.build());
80 }
81
82 @Override
83 protected void insertLocal(SignalGroupV2Record record) throws SQLException {
84 StorageRecordUpdate<SignalGroupV2Record> update = new StorageRecordUpdate<>(null, record);
85 updateLocal(update);
86 }
87
88 @Override
89 protected void updateLocal(StorageRecordUpdate<SignalGroupV2Record> update) throws SQLException {
90 final var groupV2Record = update.newRecord();
91 final var groupV2Proto = groupV2Record.getProto();
92 final var groupMasterKey = getGroupMasterKeyOrThrow(groupV2Proto.masterKey);
93
94 final var group = account.getGroupStore().getGroupOrPartialMigrate(connection, groupMasterKey);
95 group.setBlocked(groupV2Proto.blocked);
96 group.setProfileSharingEnabled(groupV2Proto.whitelisted);
97 account.getGroupStore().updateGroup(connection, group);
98 account.getGroupStore()
99 .storeStorageRecord(connection, group.getGroupId(), groupV2Record.getId(), groupV2Proto.encode());
100 }
101
102 @NotNull
103 private static GroupMasterKey getGroupMasterKeyOrThrow(final ByteString masterKey) {
104 try {
105 return new GroupMasterKey(masterKey.toByteArray());
106 } catch (InvalidInputException e) {
107 throw new AssertionError(e);
108 }
109 }
110
111 @Override
112 public int compare(SignalGroupV2Record lhs, SignalGroupV2Record rhs) {
113 if (lhs.getProto().masterKey.equals(rhs.getProto().masterKey)) {
114 return 0;
115 } else {
116 return 1;
117 }
118 }
119
120 private static boolean doProtosMatch(GroupV2Record merged, GroupV2Record other) {
121 return Arrays.equals(merged.encode(), other.encode());
122 }
123 }