mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-30 02:50:39 +00:00
Refactor group v2 migration
This commit is contained in:
parent
f06eeb01b9
commit
0d60c4d464
2 changed files with 81 additions and 44 deletions
|
@ -107,23 +107,8 @@ public class GroupHelper {
|
||||||
) {
|
) {
|
||||||
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
||||||
|
|
||||||
var groupId = GroupUtils.getGroupIdV2(groupSecretParams);
|
final var groupId = GroupUtils.getGroupIdV2(groupSecretParams);
|
||||||
var groupInfo = getGroup(groupId);
|
final var groupInfoV2 = account.getGroupStore().getGroupOrPartialMigrate(groupMasterKey, groupId);
|
||||||
final GroupInfoV2 groupInfoV2;
|
|
||||||
if (groupInfo instanceof GroupInfoV1) {
|
|
||||||
// Received a v2 group message for a v1 group, we need to locally migrate the group
|
|
||||||
account.getGroupStore().deleteGroup(((GroupInfoV1) groupInfo).getGroupId());
|
|
||||||
groupInfoV2 = new GroupInfoV2(groupId, groupMasterKey, account.getRecipientResolver());
|
|
||||||
groupInfoV2.setBlocked(groupInfo.isBlocked());
|
|
||||||
account.getGroupStore().updateGroup(groupInfoV2);
|
|
||||||
logger.info("Locally migrated group {} to group v2, id: {}",
|
|
||||||
groupInfo.getGroupId().toBase64(),
|
|
||||||
groupInfoV2.getGroupId().toBase64());
|
|
||||||
} else if (groupInfo instanceof GroupInfoV2) {
|
|
||||||
groupInfoV2 = (GroupInfoV2) groupInfo;
|
|
||||||
} else {
|
|
||||||
groupInfoV2 = new GroupInfoV2(groupId, groupMasterKey, account.getRecipientResolver());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupInfoV2.getGroup() == null || groupInfoV2.getGroup().revision < revision) {
|
if (groupInfoV2.getGroup() == null || groupInfoV2.getGroup().revision < revision) {
|
||||||
DecryptedGroup group = null;
|
DecryptedGroup group = null;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.asamk.signal.manager.storage.recipients.RecipientIdCreator;
|
||||||
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
|
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
|
||||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||||
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
||||||
|
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -87,6 +88,14 @@ public class GroupStore {
|
||||||
public void updateGroup(GroupInfo group) {
|
public void updateGroup(GroupInfo group) {
|
||||||
try (final var connection = database.getConnection()) {
|
try (final var connection = database.getConnection()) {
|
||||||
connection.setAutoCommit(false);
|
connection.setAutoCommit(false);
|
||||||
|
updateGroup(connection, group);
|
||||||
|
connection.commit();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Failed update recipient store", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateGroup(final Connection connection, final GroupInfo group) throws SQLException {
|
||||||
final Long internalId;
|
final Long internalId;
|
||||||
final var sql = (
|
final var sql = (
|
||||||
"""
|
"""
|
||||||
|
@ -100,10 +109,6 @@ public class GroupStore {
|
||||||
internalId = Utils.executeQueryForOptional(statement, res -> res.getLong("_id")).orElse(null);
|
internalId = Utils.executeQueryForOptional(statement, res -> res.getLong("_id")).orElse(null);
|
||||||
}
|
}
|
||||||
insertOrReplaceGroup(connection, internalId, group);
|
insertOrReplaceGroup(connection, internalId, group);
|
||||||
connection.commit();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException("Failed update recipient store", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteGroup(GroupId groupId) {
|
public void deleteGroup(GroupId groupId) {
|
||||||
|
@ -115,30 +120,34 @@ public class GroupStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteGroup(GroupIdV1 groupIdV1) {
|
public void deleteGroup(GroupIdV1 groupIdV1) {
|
||||||
|
try (final var connection = database.getConnection()) {
|
||||||
|
deleteGroup(connection, groupIdV1);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Failed update group store", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteGroup(final Connection connection, final GroupIdV1 groupIdV1) throws SQLException {
|
||||||
final var sql = (
|
final var sql = (
|
||||||
"""
|
"""
|
||||||
DELETE FROM %s
|
DELETE FROM %s
|
||||||
WHERE group_id = ?
|
WHERE group_id = ?
|
||||||
"""
|
"""
|
||||||
).formatted(TABLE_GROUP_V1);
|
).formatted(TABLE_GROUP_V1);
|
||||||
try (final var connection = database.getConnection()) {
|
|
||||||
try (final var statement = connection.prepareStatement(sql)) {
|
try (final var statement = connection.prepareStatement(sql)) {
|
||||||
statement.setBytes(1, groupIdV1.serialize());
|
statement.setBytes(1, groupIdV1.serialize());
|
||||||
statement.executeUpdate();
|
statement.executeUpdate();
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException("Failed update group store", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteGroup(GroupIdV2 groupIdV2) {
|
public void deleteGroup(GroupIdV2 groupIdV2) {
|
||||||
|
try (final var connection = database.getConnection()) {
|
||||||
final var sql = (
|
final var sql = (
|
||||||
"""
|
"""
|
||||||
DELETE FROM %s
|
DELETE FROM %s
|
||||||
WHERE group_id = ?
|
WHERE group_id = ?
|
||||||
"""
|
"""
|
||||||
).formatted(TABLE_GROUP_V2);
|
).formatted(TABLE_GROUP_V2);
|
||||||
try (final var connection = database.getConnection()) {
|
|
||||||
try (final var statement = connection.prepareStatement(sql)) {
|
try (final var statement = connection.prepareStatement(sql)) {
|
||||||
statement.setBytes(1, groupIdV2.serialize());
|
statement.setBytes(1, groupIdV2.serialize());
|
||||||
statement.executeUpdate();
|
statement.executeUpdate();
|
||||||
|
@ -193,6 +202,49 @@ public class GroupStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GroupInfoV2 getGroupOrPartialMigrate(
|
||||||
|
Connection connection, final GroupMasterKey groupMasterKey
|
||||||
|
) throws SQLException {
|
||||||
|
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
||||||
|
final var groupId = GroupUtils.getGroupIdV2(groupSecretParams);
|
||||||
|
|
||||||
|
return getGroupOrPartialMigrate(connection, groupMasterKey, groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupInfoV2 getGroupOrPartialMigrate(
|
||||||
|
final GroupMasterKey groupMasterKey, final GroupIdV2 groupId
|
||||||
|
) {
|
||||||
|
try (final var connection = database.getConnection()) {
|
||||||
|
return getGroupOrPartialMigrate(connection, groupMasterKey, groupId);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException("Failed read from group store", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupInfoV2 getGroupOrPartialMigrate(
|
||||||
|
Connection connection, final GroupMasterKey groupMasterKey, final GroupIdV2 groupId
|
||||||
|
) throws SQLException {
|
||||||
|
switch (getGroup(groupId)) {
|
||||||
|
case GroupInfoV1 groupInfoV1 -> {
|
||||||
|
// Received a v2 group message for a v1 group, we need to locally migrate the group
|
||||||
|
deleteGroup(connection, groupInfoV1.getGroupId());
|
||||||
|
final var groupInfoV2 = new GroupInfoV2(groupId, groupMasterKey, recipientResolver);
|
||||||
|
groupInfoV2.setBlocked(groupInfoV1.isBlocked());
|
||||||
|
updateGroup(connection, groupInfoV2);
|
||||||
|
logger.debug("Locally migrated group {} to group v2, id: {}",
|
||||||
|
groupInfoV1.getGroupId().toBase64(),
|
||||||
|
groupInfoV2.getGroupId().toBase64());
|
||||||
|
return groupInfoV2;
|
||||||
|
}
|
||||||
|
case GroupInfoV2 groupInfoV2 -> {
|
||||||
|
return groupInfoV2;
|
||||||
|
}
|
||||||
|
case null -> {
|
||||||
|
return new GroupInfoV2(groupId, groupMasterKey, recipientResolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<GroupInfo> getGroups() {
|
public List<GroupInfo> getGroups() {
|
||||||
return Stream.concat(getGroupsV2().stream(), getGroupsV1().stream()).toList();
|
return Stream.concat(getGroupsV2().stream(), getGroupsV1().stream()).toList();
|
||||||
}
|
}
|
||||||
|
@ -212,7 +264,7 @@ public class GroupStore {
|
||||||
statement.setLong(2, toBeMergedRecipientId.id());
|
statement.setLong(2, toBeMergedRecipientId.id());
|
||||||
final var updatedRows = statement.executeUpdate();
|
final var updatedRows = statement.executeUpdate();
|
||||||
if (updatedRows > 0) {
|
if (updatedRows > 0) {
|
||||||
logger.info("Updated {} group members when merging recipients", updatedRows);
|
logger.debug("Updated {} group members when merging recipients", updatedRows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue