mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-08-29 06:06:22 +00:00
Fix storage sync issues
This commit is contained in:
parent
9605c94f09
commit
3c17e01c37
@ -452,7 +452,7 @@ public class StorageHelper {
|
||||
final var stickerPacks = account.getStickerStore()
|
||||
.getStickerPacks(connection)
|
||||
.stream()
|
||||
.filter(pack -> pack.isInstalled() || pack.deletedTimestamp() > 0)
|
||||
.filter(pack -> pack.storageId() != null)
|
||||
.toList();
|
||||
newStickerPackStorageIds = generateStickerPackStorageIds(stickerPacks);
|
||||
for (final var stickerPack : stickerPacks) {
|
||||
@ -716,6 +716,13 @@ public class StorageHelper {
|
||||
final var contactRecordProcessor = new ContactRecordProcessor(account, connection, context.getJobExecutor());
|
||||
final var stickerPackRecordProcessor = new StickerPackRecordProcessor(account, connection);
|
||||
|
||||
final var contactRecords = records.stream()
|
||||
.filter(record -> record.getProto().contact != null)
|
||||
.map(record -> StorageRecordConvertersKt.toSignalContactRecord(record.getProto().contact,
|
||||
record.getId()))
|
||||
.toList();
|
||||
contactRecordProcessor.prepare(contactRecords);
|
||||
|
||||
for (final var record : records) {
|
||||
if (record.getProto().account != null) {
|
||||
logger.debug("Reading record {} of type account", record.getId());
|
||||
|
||||
@ -923,11 +923,34 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
statement.executeUpdate();
|
||||
}
|
||||
if (contact != null && contact.unregisteredTimestamp() != null) {
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId);
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId, contact.unregisteredTimestamp());
|
||||
}
|
||||
rotateStorageId(connection, recipientId);
|
||||
}
|
||||
|
||||
public void splitForStorageSyncIfNecessary(final Connection connection, final ACI aci) throws SQLException {
|
||||
final var recipient = findByServiceId(connection, aci);
|
||||
if (recipient.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var recipientId = recipient.get().id();
|
||||
final var address = recipient.get().address();
|
||||
if (address.pni().isEmpty() && address.number().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Splitting {} for storage sync", recipientId);
|
||||
final var splitAddress = new RecipientAddress(Optional.empty(),
|
||||
address.pni(),
|
||||
address.number(),
|
||||
Optional.empty());
|
||||
updateRecipientAddress(connection,
|
||||
recipientId,
|
||||
new RecipientAddress(address.aci(), Optional.empty(), Optional.empty(), address.username()));
|
||||
resolveRecipientTrusted(connection, splitAddress);
|
||||
}
|
||||
|
||||
public int removeStorageIdsFromLocalOnlyUnregisteredRecipients(
|
||||
final Connection connection,
|
||||
final Collection<StorageId> storageIds
|
||||
@ -1063,7 +1086,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
if (recipientAddress.get().address().aci().isEmpty() || (
|
||||
contact != null && contact.unregisteredTimestamp() != null
|
||||
)) {
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId);
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId, System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1097,7 +1120,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
if (registered) {
|
||||
markRegistered(connection, recipientId);
|
||||
} else {
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId);
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId, System.currentTimeMillis());
|
||||
}
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
@ -1107,9 +1130,10 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
|
||||
private void markUnregisteredAndSplitIfNecessary(
|
||||
final Connection connection,
|
||||
final RecipientId recipientId
|
||||
final RecipientId recipientId,
|
||||
final long unregisteredTimestamp
|
||||
) throws SQLException {
|
||||
markUnregistered(connection, recipientId);
|
||||
markUnregistered(connection, recipientId, unregisteredTimestamp);
|
||||
final var address = resolveRecipientAddress(connection, recipientId);
|
||||
final var needSplit = address.aci().isPresent() && address.pni().isPresent();
|
||||
logger.trace("Marking unregistered recipient {} as unregistered (and split={}): {}",
|
||||
@ -1156,7 +1180,11 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
}
|
||||
}
|
||||
|
||||
private void markUnregistered(final Connection connection, final RecipientId recipientId) throws SQLException {
|
||||
private void markUnregistered(
|
||||
final Connection connection,
|
||||
final RecipientId recipientId,
|
||||
final long unregisteredTimestamp
|
||||
) throws SQLException {
|
||||
final var sql = (
|
||||
"""
|
||||
UPDATE %s
|
||||
@ -1165,7 +1193,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setLong(1, System.currentTimeMillis());
|
||||
statement.setLong(1, unregisteredTimestamp);
|
||||
statement.setLong(2, recipientId.id());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
@ -298,7 +298,7 @@ public class StickerStore {
|
||||
"""
|
||||
SELECT s.pack_id
|
||||
FROM %s s
|
||||
WHERE s.storage_id IS NULL AND (s.installed = TRUE OR s.deleted_timestamp > 0)
|
||||
WHERE s.storage_id IS NULL AND s.installed = TRUE
|
||||
"""
|
||||
).formatted(TABLE_STICKER);
|
||||
final var updateSql = (
|
||||
|
||||
@ -24,6 +24,7 @@ import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.Id
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
@ -56,6 +57,29 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
||||
this.selfNumber = account.getNumber();
|
||||
}
|
||||
|
||||
public void prepare(final Collection<SignalContactRecord> remoteRecords) throws SQLException {
|
||||
for (final var remoteRecord : remoteRecords) {
|
||||
if (isInvalid(remoteRecord)) {
|
||||
continue;
|
||||
}
|
||||
final var remote = remoteRecord.getProto();
|
||||
final var aci = ACI.parseOrNull(remote.aci, remote.aciBinary);
|
||||
final var pni = PNI.parseOrNull(remote.pni, remote.pniBinary);
|
||||
if (shouldSplitForStorageSync(remote.unregisteredAtTimestamp, aci, pni, remote.e164)) {
|
||||
account.getRecipientStore().splitForStorageSyncIfNecessary(connection, aci);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean shouldSplitForStorageSync(
|
||||
final long unregisteredAtTimestamp,
|
||||
final ACI aci,
|
||||
final PNI pni,
|
||||
final String e164
|
||||
) {
|
||||
return unregisteredAtTimestamp > 0 && aci != null && pni == null && e164.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Error cases:
|
||||
* - You can't have a contact record without an ACI or PNI.
|
||||
|
||||
@ -63,16 +63,17 @@ public class StickerPackRecordProcessor extends DefaultStorageRecordProcessor<Si
|
||||
final var remote = remoteRecord.getProto();
|
||||
final var local = localRecord.getProto();
|
||||
|
||||
final var isRemoteDeleted = remote.deletedAtTimestamp > 0;
|
||||
final var isLocalDeleted = local.deletedAtTimestamp > 0;
|
||||
|
||||
if (isRemoteDeleted && isLocalDeleted && local.deletedAtTimestamp > remote.deletedAtTimestamp) {
|
||||
if (shouldKeepLocalDeletion(remote.deletedAtTimestamp, local.deletedAtTimestamp)) {
|
||||
return localRecord;
|
||||
}
|
||||
|
||||
return remoteRecord;
|
||||
}
|
||||
|
||||
static boolean shouldKeepLocalDeletion(final long remoteDeletedAt, final long localDeletedAt) {
|
||||
return remoteDeletedAt > 0 && localDeletedAt > 0 && localDeletedAt < remoteDeletedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void insertLocal(final SignalStickerPackRecord record) throws SQLException {
|
||||
account.getStickerStore().upsertFromStorageSync(connection, record);
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package org.asamk.signal.manager.syncStorage;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.signal.core.models.ServiceId.ACI;
|
||||
import org.signal.core.models.ServiceId.PNI;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class StorageRecordProcessorTest {
|
||||
|
||||
@Test
|
||||
void splitsOnlyUnregisteredAciOnlyRecords() {
|
||||
final var aci = ACI.from(UUID.randomUUID());
|
||||
final var pni = PNI.from(UUID.randomUUID());
|
||||
|
||||
assertTrue(ContactRecordProcessor.shouldSplitForStorageSync(1, aci, null, ""));
|
||||
assertFalse(ContactRecordProcessor.shouldSplitForStorageSync(0, aci, null, ""));
|
||||
assertFalse(ContactRecordProcessor.shouldSplitForStorageSync(1, null, null, ""));
|
||||
assertFalse(ContactRecordProcessor.shouldSplitForStorageSync(1, aci, pni, ""));
|
||||
assertFalse(ContactRecordProcessor.shouldSplitForStorageSync(1, aci, null, "+12025550123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsOlderLocalStickerDeletion() {
|
||||
assertTrue(StickerPackRecordProcessor.shouldKeepLocalDeletion(200, 100));
|
||||
assertFalse(StickerPackRecordProcessor.shouldKeepLocalDeletion(100, 200));
|
||||
assertFalse(StickerPackRecordProcessor.shouldKeepLocalDeletion(200, 0));
|
||||
assertFalse(StickerPackRecordProcessor.shouldKeepLocalDeletion(0, 100));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user