mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
Fix storage sync issues
This commit is contained in:
parent
f92466f6be
commit
90df256e85
8 changed files with 67 additions and 51 deletions
|
@ -155,7 +155,7 @@ public class StorageHelper {
|
||||||
|
|
||||||
if (updated > 0) {
|
if (updated > 0) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Found {} records that were deleted remotely but only marked unregistered locally. Removed those from local store. Recalculating diff.",
|
"Found {} records that were deleted remotely but only marked unregistered locally. Removed those from local store.",
|
||||||
updated);
|
updated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ public class StorageHelper {
|
||||||
final var remote = remoteByRawId.get(rawId);
|
final var remote = remoteByRawId.get(rawId);
|
||||||
final var local = localByRawId.get(rawId);
|
final var local = localByRawId.get(rawId);
|
||||||
|
|
||||||
if (remote.getType() != local.getType()) {
|
if (remote.getType() != local.getType() && local.getType() != 0) {
|
||||||
remoteOnlyRawIds.remove(rawId);
|
remoteOnlyRawIds.remove(rawId);
|
||||||
localOnlyRawIds.remove(rawId);
|
localOnlyRawIds.remove(rawId);
|
||||||
hasTypeMismatch = true;
|
hasTypeMismatch = true;
|
||||||
|
|
|
@ -324,7 +324,12 @@ public class SyncHelper {
|
||||||
final var recipientId = account.getRecipientTrustedResolver().resolveRecipientTrusted(c.getAddress());
|
final var recipientId = account.getRecipientTrustedResolver().resolveRecipientTrusted(c.getAddress());
|
||||||
var contact = account.getContactStore().getContact(recipientId);
|
var contact = account.getContactStore().getContact(recipientId);
|
||||||
final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
|
final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
|
||||||
if (c.getName().isPresent()) {
|
if (c.getName().isPresent() && (
|
||||||
|
contact == null || (
|
||||||
|
contact.givenName() == null
|
||||||
|
&& contact.familyName() == null
|
||||||
|
)
|
||||||
|
)) {
|
||||||
builder.withGivenName(c.getName().get());
|
builder.withGivenName(c.getName().get());
|
||||||
builder.withFamilyName(null);
|
builder.withFamilyName(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1355,8 +1355,8 @@ public class ManagerImpl implements Manager {
|
||||||
if (thread != null) {
|
if (thread != null) {
|
||||||
stopReceiveThread(thread);
|
stopReceiveThread(thread);
|
||||||
}
|
}
|
||||||
executor.close();
|
|
||||||
context.close();
|
context.close();
|
||||||
|
executor.close();
|
||||||
|
|
||||||
dependencies.getSignalWebSocket().disconnect();
|
dependencies.getSignalWebSocket().disconnect();
|
||||||
dependencies.getPushServiceSocket().close();
|
dependencies.getPushServiceSocket().close();
|
||||||
|
|
|
@ -362,7 +362,7 @@ public class SignalAccount implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
this.selfRecipientId = getRecipientResolver().resolveRecipient(getSelfRecipientAddress());
|
this.selfRecipientId = getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void migrateLegacyConfigs() {
|
private void migrateLegacyConfigs() {
|
||||||
|
|
|
@ -122,13 +122,25 @@ public class GroupStore {
|
||||||
public void storeStorageRecord(
|
public void storeStorageRecord(
|
||||||
final Connection connection, final GroupId groupId, final StorageId storageId, final byte[] storageRecord
|
final Connection connection, final GroupId groupId, final StorageId storageId, final byte[] storageRecord
|
||||||
) throws SQLException {
|
) throws SQLException {
|
||||||
|
final var groupTable = groupId instanceof GroupIdV1 ? TABLE_GROUP_V1 : TABLE_GROUP_V2;
|
||||||
|
final var deleteSql = (
|
||||||
|
"""
|
||||||
|
UPDATE %s
|
||||||
|
SET storage_id = NULL
|
||||||
|
WHERE storage_id = ?
|
||||||
|
"""
|
||||||
|
).formatted(groupTable);
|
||||||
|
try (final var statement = connection.prepareStatement(deleteSql)) {
|
||||||
|
statement.setBytes(1, storageId.getRaw());
|
||||||
|
statement.executeUpdate();
|
||||||
|
}
|
||||||
final var sql = (
|
final var sql = (
|
||||||
"""
|
"""
|
||||||
UPDATE %s
|
UPDATE %s
|
||||||
SET storage_id = ?, storage_record = ?
|
SET storage_id = ?, storage_record = ?
|
||||||
WHERE group_id = ?
|
WHERE group_id = ?
|
||||||
"""
|
"""
|
||||||
).formatted(groupId instanceof GroupIdV1 ? TABLE_GROUP_V1 : TABLE_GROUP_V2);
|
).formatted(groupTable);
|
||||||
try (final var statement = connection.prepareStatement(sql)) {
|
try (final var statement = connection.prepareStatement(sql)) {
|
||||||
statement.setBytes(1, storageId.getRaw());
|
statement.setBytes(1, storageId.getRaw());
|
||||||
if (storageRecord == null) {
|
if (storageRecord == null) {
|
||||||
|
|
|
@ -37,8 +37,6 @@ import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.asamk.signal.manager.config.ServiceConfig.UNREGISTERED_LIFESPAN;
|
|
||||||
|
|
||||||
public class RecipientStore implements RecipientIdCreator, RecipientResolver, RecipientTrustedResolver, ContactsStore, ProfileStore {
|
public class RecipientStore implements RecipientIdCreator, RecipientResolver, RecipientTrustedResolver, ContactsStore, ProfileStore {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RecipientStore.class);
|
private static final Logger logger = LoggerFactory.getLogger(RecipientStore.class);
|
||||||
|
@ -524,7 +522,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||||
"""
|
"""
|
||||||
SELECT r._id
|
SELECT r._id
|
||||||
FROM %s r
|
FROM %s r
|
||||||
WHERE r.storage_id IS NULL AND (r.unregistered_timestamp IS NULL OR r.unregistered_timestamp > ?)
|
WHERE r.storage_id IS NULL AND r.unregistered_timestamp IS NULL
|
||||||
"""
|
"""
|
||||||
).formatted(TABLE_RECIPIENT);
|
).formatted(TABLE_RECIPIENT);
|
||||||
final var updateSql = (
|
final var updateSql = (
|
||||||
|
@ -537,7 +535,6 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||||
try (final var connection = database.getConnection()) {
|
try (final var connection = database.getConnection()) {
|
||||||
connection.setAutoCommit(false);
|
connection.setAutoCommit(false);
|
||||||
try (final var selectStmt = connection.prepareStatement(selectSql)) {
|
try (final var selectStmt = connection.prepareStatement(selectSql)) {
|
||||||
selectStmt.setLong(1, System.currentTimeMillis() - UNREGISTERED_LIFESPAN);
|
|
||||||
final var recipientIds = Utils.executeQueryForStream(selectStmt, this::getRecipientIdFromResultSet)
|
final var recipientIds = Utils.executeQueryForStream(selectStmt, this::getRecipientIdFromResultSet)
|
||||||
.toList();
|
.toList();
|
||||||
try (final var updateStmt = connection.prepareStatement(updateSql)) {
|
try (final var updateStmt = connection.prepareStatement(updateSql)) {
|
||||||
|
@ -735,14 +732,25 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||||
final StorageId storageId,
|
final StorageId storageId,
|
||||||
final byte[] storageRecord
|
final byte[] storageRecord
|
||||||
) throws SQLException {
|
) throws SQLException {
|
||||||
final var sql = (
|
final var deleteSql = (
|
||||||
|
"""
|
||||||
|
UPDATE %s
|
||||||
|
SET storage_id = NULL
|
||||||
|
WHERE storage_id = ?
|
||||||
|
"""
|
||||||
|
).formatted(TABLE_RECIPIENT);
|
||||||
|
try (final var statement = connection.prepareStatement(deleteSql)) {
|
||||||
|
statement.setBytes(1, storageId.getRaw());
|
||||||
|
statement.executeUpdate();
|
||||||
|
}
|
||||||
|
final var insertSql = (
|
||||||
"""
|
"""
|
||||||
UPDATE %s
|
UPDATE %s
|
||||||
SET storage_id = ?, storage_record = ?
|
SET storage_id = ?, storage_record = ?
|
||||||
WHERE _id = ?
|
WHERE _id = ?
|
||||||
"""
|
"""
|
||||||
).formatted(TABLE_RECIPIENT);
|
).formatted(TABLE_RECIPIENT);
|
||||||
try (final var statement = connection.prepareStatement(sql)) {
|
try (final var statement = connection.prepareStatement(insertSql)) {
|
||||||
statement.setBytes(1, storageId.getRaw());
|
statement.setBytes(1, storageId.getRaw());
|
||||||
if (storageRecord == null) {
|
if (storageRecord == null) {
|
||||||
statement.setNull(2, Types.BLOB);
|
statement.setNull(2, Types.BLOB);
|
||||||
|
@ -846,7 +854,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||||
"""
|
"""
|
||||||
UPDATE %s
|
UPDATE %s
|
||||||
SET storage_id = NULL
|
SET storage_id = NULL
|
||||||
WHERE storage_id = ? AND storage_id IS NOT NULL AND unregistered_timestamp IS NOT NULL
|
WHERE storage_id = ? AND unregistered_timestamp IS NOT NULL
|
||||||
"""
|
"""
|
||||||
).formatted(TABLE_RECIPIENT);
|
).formatted(TABLE_RECIPIENT);
|
||||||
var count = 0;
|
var count = 0;
|
||||||
|
@ -1002,6 +1010,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pair.second().isEmpty()) {
|
if (!pair.second().isEmpty()) {
|
||||||
|
logger.debug("Resolved address {}, merging {} other recipients", address, pair.second().size());
|
||||||
try (final var connection = database.getConnection()) {
|
try (final var connection = database.getConnection()) {
|
||||||
connection.setAutoCommit(false);
|
connection.setAutoCommit(false);
|
||||||
mergeRecipients(connection, pair.first(), pair.second());
|
mergeRecipients(connection, pair.first(), pair.second());
|
||||||
|
|
|
@ -105,8 +105,12 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
||||||
|
|
||||||
IdentityState identityState;
|
IdentityState identityState;
|
||||||
byte[] identityKey;
|
byte[] identityKey;
|
||||||
if ((remote.getIdentityState() != local.getIdentityState() && remote.getIdentityKey().isPresent())
|
if (remote.getIdentityKey().isPresent() && (
|
||||||
|| (remote.getIdentityKey().isPresent() && local.getIdentityKey().isEmpty())) {
|
remote.getIdentityState() != local.getIdentityState()
|
||||||
|
|| local.getIdentityKey().isEmpty()
|
||||||
|
|| !account.isPrimaryDevice()
|
||||||
|
|
||||||
|
)) {
|
||||||
identityState = remote.getIdentityState();
|
identityState = remote.getIdentityState();
|
||||||
identityKey = remote.getIdentityKey().get();
|
identityKey = remote.getIdentityKey().get();
|
||||||
} else {
|
} else {
|
||||||
|
@ -114,9 +118,10 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
||||||
identityKey = local.getIdentityKey().orElse(null);
|
identityKey = local.getIdentityKey().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local.getAci().isPresent() && identityKey != null && remote.getIdentityKey().isPresent() && !Arrays.equals(
|
if (local.getAci().isPresent()
|
||||||
identityKey,
|
&& local.getIdentityKey().isPresent()
|
||||||
remote.getIdentityKey().get())) {
|
&& remote.getIdentityKey().isPresent()
|
||||||
|
&& !Arrays.equals(local.getIdentityKey().get(), remote.getIdentityKey().get())) {
|
||||||
logger.debug("The local and remote identity keys do not match for {}. Enqueueing a profile fetch.",
|
logger.debug("The local and remote identity keys do not match for {}. Enqueueing a profile fetch.",
|
||||||
local.getAci().orElse(null));
|
local.getAci().orElse(null));
|
||||||
final var address = getRecipientAddress(local);
|
final var address = getRecipientAddress(local);
|
||||||
|
@ -141,13 +146,12 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
||||||
|
|
||||||
PNI pni;
|
PNI pni;
|
||||||
String e164;
|
String e164;
|
||||||
|
if (!account.isPrimaryDevice() && (e164sMatchButPnisDont || pnisMatchButE164sDont)) {
|
||||||
if (e164sMatchButPnisDont) {
|
if (e164sMatchButPnisDont) {
|
||||||
logger.debug("Matching E164s, but the PNIs differ! Trusting our local pair.");
|
logger.debug("Matching E164s, but the PNIs differ! Trusting our local pair.");
|
||||||
// TODO [pnp] Schedule CDS fetch?
|
|
||||||
pni = local.getPni().get();
|
|
||||||
e164 = local.getNumber().get();
|
|
||||||
} else if (pnisMatchButE164sDont) {
|
} else if (pnisMatchButE164sDont) {
|
||||||
logger.debug("Matching PNIs, but the E164s differ! Trusting our local pair.");
|
logger.debug("Matching PNIs, but the E164s differ! Trusting our local pair.");
|
||||||
|
}
|
||||||
// TODO [pnp] Schedule CDS fetch?
|
// TODO [pnp] Schedule CDS fetch?
|
||||||
pni = local.getPni().get();
|
pni = local.getPni().get();
|
||||||
e164 = local.getNumber().get();
|
e164 = local.getNumber().get();
|
||||||
|
@ -235,41 +239,25 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
||||||
|| profileShared != contactRecord.isProfileSharingEnabled()
|
|| profileShared != contactRecord.isProfileSharingEnabled()
|
||||||
|| archived != contactRecord.isArchived()
|
|| archived != contactRecord.isArchived()
|
||||||
|| hidden != contactRecord.isHidden()
|
|| hidden != contactRecord.isHidden()
|
||||||
|| (
|
|| !Objects.equals(contactRecord.getSystemGivenName().orElse(null), contactGivenName)
|
||||||
contactRecord.getSystemGivenName().isPresent() && !contactRecord.getSystemGivenName()
|
|| !Objects.equals(contactRecord.getSystemFamilyName().orElse(null), contactFamilyName)) {
|
||||||
.get()
|
|
||||||
.equals(contactGivenName)
|
|
||||||
)
|
|
||||||
|| (
|
|
||||||
contactRecord.getSystemFamilyName().isPresent() && !contactRecord.getSystemFamilyName()
|
|
||||||
.get()
|
|
||||||
.equals(contactFamilyName)
|
|
||||||
)) {
|
|
||||||
logger.debug("Storing new or updated contact {}", recipientId);
|
logger.debug("Storing new or updated contact {}", recipientId);
|
||||||
final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
|
final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
|
||||||
final var newContact = contactBuilder.withIsBlocked(contactRecord.isBlocked())
|
final var newContact = contactBuilder.withIsBlocked(contactRecord.isBlocked())
|
||||||
.withIsProfileSharingEnabled(contactRecord.isProfileSharingEnabled())
|
.withIsProfileSharingEnabled(contactRecord.isProfileSharingEnabled())
|
||||||
.withIsArchived(contactRecord.isArchived())
|
.withIsArchived(contactRecord.isArchived())
|
||||||
.withIsHidden(contactRecord.isHidden());
|
.withIsHidden(contactRecord.isHidden())
|
||||||
if (contactRecord.getSystemGivenName().isPresent() || contactRecord.getSystemFamilyName().isPresent()) {
|
.withGivenName(contactRecord.getSystemGivenName().orElse(null))
|
||||||
newContact.withGivenName(contactRecord.getSystemGivenName().orElse(null))
|
|
||||||
.withFamilyName(contactRecord.getSystemFamilyName().orElse(null));
|
.withFamilyName(contactRecord.getSystemFamilyName().orElse(null));
|
||||||
}
|
|
||||||
account.getRecipientStore().storeContact(connection, recipientId, newContact.build());
|
account.getRecipientStore().storeContact(connection, recipientId, newContact.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
final var profile = recipient.getProfile();
|
final var profile = recipient.getProfile();
|
||||||
final var profileGivenName = profile == null ? null : profile.getGivenName();
|
final var profileGivenName = profile == null ? null : profile.getGivenName();
|
||||||
final var profileFamilyName = profile == null ? null : profile.getFamilyName();
|
final var profileFamilyName = profile == null ? null : profile.getFamilyName();
|
||||||
if ((
|
if (!Objects.equals(contactRecord.getProfileGivenName().orElse(null), profileGivenName) || !Objects.equals(
|
||||||
contactRecord.getProfileGivenName().isPresent() && !contactRecord.getProfileGivenName()
|
contactRecord.getProfileFamilyName().orElse(null),
|
||||||
.get()
|
profileFamilyName)) {
|
||||||
.equals(profileGivenName)
|
|
||||||
) || (
|
|
||||||
contactRecord.getProfileFamilyName().isPresent() && !contactRecord.getProfileFamilyName()
|
|
||||||
.get()
|
|
||||||
.equals(profileFamilyName)
|
|
||||||
)) {
|
|
||||||
final var profileBuilder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile);
|
final var profileBuilder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile);
|
||||||
final var newProfile = profileBuilder.withGivenName(contactRecord.getProfileGivenName().orElse(null))
|
final var newProfile = profileBuilder.withGivenName(contactRecord.getProfileGivenName().orElse(null))
|
||||||
.withFamilyName(contactRecord.getProfileFamilyName().orElse(null))
|
.withFamilyName(contactRecord.getProfileFamilyName().orElse(null))
|
||||||
|
@ -285,7 +273,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
||||||
logger.warn("Received invalid contact profile key from storage");
|
logger.warn("Received invalid contact profile key from storage");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (contactRecord.getIdentityKey().isPresent() && contactRecord.getAci().orElse(null) != null) {
|
if (contactRecord.getIdentityKey().isPresent() && contactRecord.getAci().isPresent()) {
|
||||||
try {
|
try {
|
||||||
logger.trace("Storing identity key {}", recipientId);
|
logger.trace("Storing identity key {}", recipientId);
|
||||||
final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get());
|
final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get());
|
||||||
|
|
|
@ -55,7 +55,9 @@ abstract class DefaultStorageRecordProcessor<E extends SignalRecord> implements
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchedRecords.contains(local.get())) {
|
if (matchedRecords.contains(local.get())) {
|
||||||
debug(remote.getId(), remote, "Multiple remote records map to the same local record! Ignoring this one.");
|
debug(remote.getId(),
|
||||||
|
remote,
|
||||||
|
"Multiple remote records map to the same local record " + local.get() + "! Ignoring this one.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue