Store recipient unregistered state
This commit is contained in:
		
							parent
							
								
									6a5dcd00b2
								
							
						
					
					
						commit
						f92466f6be
					
				@ -24,6 +24,7 @@ public class ServiceConfig {
 | 
			
		||||
    public static final boolean AUTOMATIC_NETWORK_RETRY = true;
 | 
			
		||||
    public static final int GROUP_MAX_SIZE = 1001;
 | 
			
		||||
    public static final int MAXIMUM_ONE_OFF_REQUEST_SIZE = 3;
 | 
			
		||||
    public static final long UNREGISTERED_LIFESPAN = TimeUnit.DAYS.toMillis(30);
 | 
			
		||||
 | 
			
		||||
    public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) {
 | 
			
		||||
        final var giftBadges = !isPrimaryDevice;
 | 
			
		||||
 | 
			
		||||
@ -164,6 +164,10 @@ public class RecipientHelper {
 | 
			
		||||
        registeredUsers.forEach((number, u) -> account.getRecipientTrustedResolver()
 | 
			
		||||
                .resolveRecipientTrusted(u.aci, u.pni, Optional.of(number)));
 | 
			
		||||
 | 
			
		||||
        final var unregisteredUsers = new HashSet<>(numbers);
 | 
			
		||||
        unregisteredUsers.removeAll(registeredUsers.keySet());
 | 
			
		||||
        account.getRecipientStore().markUnregistered(unregisteredUsers);
 | 
			
		||||
 | 
			
		||||
        return registeredUsers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -149,6 +149,17 @@ public class StorageHelper {
 | 
			
		||||
 | 
			
		||||
            logger.debug("Pre-Merge ID Difference :: " + idDifference);
 | 
			
		||||
 | 
			
		||||
            if (!idDifference.localOnlyIds().isEmpty()) {
 | 
			
		||||
                final var updated = account.getRecipientStore()
 | 
			
		||||
                        .removeStorageIdsFromLocalOnlyUnregisteredRecipients(connection, idDifference.localOnlyIds());
 | 
			
		||||
 | 
			
		||||
                if (updated > 0) {
 | 
			
		||||
                    logger.warn(
 | 
			
		||||
                            "Found {} records that were deleted remotely but only marked unregistered locally. Removed those from local store. Recalculating diff.",
 | 
			
		||||
                            updated);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!idDifference.isEmpty()) {
 | 
			
		||||
                final var remoteOnlyRecords = getSignalStorageRecords(storageKey, idDifference.remoteOnlyIds());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ import java.util.UUID;
 | 
			
		||||
public class AccountDatabase extends Database {
 | 
			
		||||
 | 
			
		||||
    private static final Logger logger = LoggerFactory.getLogger(AccountDatabase.class);
 | 
			
		||||
    private static final long DATABASE_VERSION = 20;
 | 
			
		||||
    private static final long DATABASE_VERSION = 21;
 | 
			
		||||
 | 
			
		||||
    private AccountDatabase(final HikariDataSource dataSource) {
 | 
			
		||||
        super(logger, DATABASE_VERSION, dataSource);
 | 
			
		||||
@ -558,5 +558,14 @@ public class AccountDatabase extends Database {
 | 
			
		||||
                                        """);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (oldVersion < 21) {
 | 
			
		||||
            logger.debug("Updating database: Create unregistered column");
 | 
			
		||||
            try (final var statement = connection.createStatement()) {
 | 
			
		||||
                statement.executeUpdate("""
 | 
			
		||||
                                        ALTER TABLE recipient ADD unregistered_timestamp INTEGER;
 | 
			
		||||
                                        UPDATE recipient SET pni = NULL WHERE uuid IS NOT NULL;
 | 
			
		||||
                                        """);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,8 @@ import java.util.Set;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import static org.asamk.signal.manager.config.ServiceConfig.UNREGISTERED_LIFESPAN;
 | 
			
		||||
 | 
			
		||||
public class RecipientStore implements RecipientIdCreator, RecipientResolver, RecipientTrustedResolver, ContactsStore, ProfileStore {
 | 
			
		||||
 | 
			
		||||
    private static final Logger logger = LoggerFactory.getLogger(RecipientStore.class);
 | 
			
		||||
@ -65,6 +67,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
 | 
			
		||||
                                      username TEXT UNIQUE,
 | 
			
		||||
                                      uuid BLOB UNIQUE,
 | 
			
		||||
                                      pni BLOB UNIQUE,
 | 
			
		||||
                                      unregistered_timestamp INTEGER,
 | 
			
		||||
                                      profile_key BLOB,
 | 
			
		||||
                                      profile_key_credential BLOB,
 | 
			
		||||
 | 
			
		||||
@ -521,7 +524,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
 | 
			
		||||
                """
 | 
			
		||||
                SELECT r._id
 | 
			
		||||
                FROM %s r
 | 
			
		||||
                WHERE r.storage_id IS NULL
 | 
			
		||||
                WHERE r.storage_id IS NULL AND (r.unregistered_timestamp IS NULL OR r.unregistered_timestamp > ?)
 | 
			
		||||
                """
 | 
			
		||||
        ).formatted(TABLE_RECIPIENT);
 | 
			
		||||
        final var updateSql = (
 | 
			
		||||
@ -534,6 +537,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
 | 
			
		||||
        try (final var connection = database.getConnection()) {
 | 
			
		||||
            connection.setAutoCommit(false);
 | 
			
		||||
            try (final var selectStmt = connection.prepareStatement(selectSql)) {
 | 
			
		||||
                selectStmt.setLong(1, System.currentTimeMillis() - UNREGISTERED_LIFESPAN);
 | 
			
		||||
                final var recipientIds = Utils.executeQueryForStream(selectStmt, this::getRecipientIdFromResultSet)
 | 
			
		||||
                        .toList();
 | 
			
		||||
                try (final var updateStmt = connection.prepareStatement(updateSql)) {
 | 
			
		||||
@ -835,6 +839,76 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
 | 
			
		||||
        rotateStorageId(connection, recipientId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int removeStorageIdsFromLocalOnlyUnregisteredRecipients(
 | 
			
		||||
            final Connection connection, final List<StorageId> storageIds
 | 
			
		||||
    ) throws SQLException {
 | 
			
		||||
        final var sql = (
 | 
			
		||||
                """
 | 
			
		||||
                UPDATE %s
 | 
			
		||||
                SET storage_id = NULL
 | 
			
		||||
                WHERE storage_id = ? AND storage_id IS NOT NULL AND unregistered_timestamp IS NOT NULL
 | 
			
		||||
                """
 | 
			
		||||
        ).formatted(TABLE_RECIPIENT);
 | 
			
		||||
        var count = 0;
 | 
			
		||||
        try (final var statement = connection.prepareStatement(sql)) {
 | 
			
		||||
            for (final var storageId : storageIds) {
 | 
			
		||||
                statement.setBytes(1, storageId.getRaw());
 | 
			
		||||
                count += statement.executeUpdate();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return count;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void markUnregistered(final Set<String> unregisteredUsers) {
 | 
			
		||||
        logger.debug("Marking {} numbers as unregistered", unregisteredUsers.size());
 | 
			
		||||
        try (final var connection = database.getConnection()) {
 | 
			
		||||
            connection.setAutoCommit(false);
 | 
			
		||||
            for (final var number : unregisteredUsers) {
 | 
			
		||||
                final var recipient = findByNumber(connection, number);
 | 
			
		||||
                if (recipient.isPresent()) {
 | 
			
		||||
                    markUnregistered(connection, recipient.get().id());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            connection.commit();
 | 
			
		||||
        } catch (SQLException e) {
 | 
			
		||||
            throw new RuntimeException("Failed update recipient store", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void markRegistered(
 | 
			
		||||
            final Connection connection, final RecipientId recipientId
 | 
			
		||||
    ) throws SQLException {
 | 
			
		||||
        final var sql = (
 | 
			
		||||
                """
 | 
			
		||||
                UPDATE %s
 | 
			
		||||
                SET unregistered_timestamp = ?
 | 
			
		||||
                WHERE _id = ?
 | 
			
		||||
                """
 | 
			
		||||
        ).formatted(TABLE_RECIPIENT);
 | 
			
		||||
        try (final var statement = connection.prepareStatement(sql)) {
 | 
			
		||||
            statement.setNull(1, Types.INTEGER);
 | 
			
		||||
            statement.setLong(2, recipientId.id());
 | 
			
		||||
            statement.executeUpdate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void markUnregistered(
 | 
			
		||||
            final Connection connection, final RecipientId recipientId
 | 
			
		||||
    ) throws SQLException {
 | 
			
		||||
        final var sql = (
 | 
			
		||||
                """
 | 
			
		||||
                UPDATE %s
 | 
			
		||||
                SET unregistered_timestamp = ?
 | 
			
		||||
                WHERE _id = ? AND unregistered_timestamp IS NULL
 | 
			
		||||
                """
 | 
			
		||||
        ).formatted(TABLE_RECIPIENT);
 | 
			
		||||
        try (final var statement = connection.prepareStatement(sql)) {
 | 
			
		||||
            statement.setLong(1, System.currentTimeMillis());
 | 
			
		||||
            statement.setLong(2, recipientId.id());
 | 
			
		||||
            statement.executeUpdate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void storeExpiringProfileKeyCredential(
 | 
			
		||||
            final Connection connection,
 | 
			
		||||
            final RecipientId recipientId,
 | 
			
		||||
@ -948,6 +1022,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
 | 
			
		||||
            return new Pair<>(resolveRecipientLocked(connection, address), List.of());
 | 
			
		||||
        } else {
 | 
			
		||||
            final var pair = MergeRecipientHelper.resolveRecipientTrustedLocked(new HelperStore(connection), address);
 | 
			
		||||
            markRegistered(connection, pair.first());
 | 
			
		||||
 | 
			
		||||
            for (final var toBeMergedRecipientId : pair.second()) {
 | 
			
		||||
                mergeRecipientsLocked(connection, pair.first(), toBeMergedRecipientId);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user