mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
Store serviceId in protocol stores as TEXT
ServiceIds are no longer just UUIDs, they can now have prefixes like "PNI:"
This commit is contained in:
parent
a7744e837c
commit
b2a32666e9
17 changed files with 287 additions and 144 deletions
|
@ -2,6 +2,7 @@ package org.asamk.signal.manager.storage;
|
|||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import org.asamk.signal.manager.api.Pair;
|
||||
import org.asamk.signal.manager.storage.groups.GroupStore;
|
||||
import org.asamk.signal.manager.storage.identities.IdentityKeyStore;
|
||||
import org.asamk.signal.manager.storage.prekeys.KyberPreKeyStore;
|
||||
|
@ -15,16 +16,21 @@ import org.asamk.signal.manager.storage.sessions.SessionStore;
|
|||
import org.asamk.signal.manager.storage.stickers.StickerStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AccountDatabase extends Database {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(AccountDatabase.class);
|
||||
private static final long DATABASE_VERSION = 14;
|
||||
private static final long DATABASE_VERSION = 15;
|
||||
|
||||
private AccountDatabase(final HikariDataSource dataSource) {
|
||||
super(logger, DATABASE_VERSION, dataSource);
|
||||
|
@ -346,7 +352,146 @@ public class AccountDatabase extends Database {
|
|||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion < 15) {
|
||||
logger.debug("Updating database: Store serviceId as TEXT");
|
||||
try (final var statement = connection.createStatement()) {
|
||||
statement.executeUpdate("""
|
||||
CREATE TABLE tmp_mapping_table (
|
||||
uuid BLOB NOT NULL,
|
||||
address TEXT NOT NULL
|
||||
) STRICT;
|
||||
""");
|
||||
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT r.uuid, r.pni
|
||||
FROM recipient r
|
||||
"""
|
||||
);
|
||||
final var uuidAddressMapping = new HashMap<UUID, ServiceId>();
|
||||
try (final var preparedStatement = connection.prepareStatement(sql)) {
|
||||
try (var result = Utils.executeQueryForStream(preparedStatement, (resultSet) -> {
|
||||
final var pni = Optional.ofNullable(resultSet.getBytes("pni"))
|
||||
.map(UuidUtil::parseOrNull)
|
||||
.map(ServiceId.PNI::from);
|
||||
final var serviceIdUuid = Optional.ofNullable(resultSet.getBytes("uuid"))
|
||||
.map(UuidUtil::parseOrNull);
|
||||
final var serviceId = serviceIdUuid.isPresent() && pni.isPresent() && serviceIdUuid.get()
|
||||
.equals(pni.get().getRawUuid())
|
||||
? pni.<ServiceId>map(p -> p)
|
||||
: serviceIdUuid.<ServiceId>map(ACI::from);
|
||||
|
||||
return new Pair<>(serviceId, pni);
|
||||
})) {
|
||||
result.forEach(p -> {
|
||||
final var serviceId = p.first();
|
||||
final var pni = p.second();
|
||||
if (serviceId.isPresent()) {
|
||||
uuidAddressMapping.put(serviceId.get().getRawUuid(), serviceId.get());
|
||||
}
|
||||
if (pni.isPresent()) {
|
||||
uuidAddressMapping.put(pni.get().getRawUuid(), pni.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final var insertSql = """
|
||||
INSERT INTO tmp_mapping_table (uuid, address)
|
||||
VALUES (?,?)
|
||||
""";
|
||||
try (final var insertStatement = connection.prepareStatement(insertSql)) {
|
||||
for (final var entry : uuidAddressMapping.entrySet()) {
|
||||
final var uuid = entry.getKey();
|
||||
final var serviceId = entry.getValue();
|
||||
insertStatement.setBytes(1, UuidUtil.toByteArray(uuid));
|
||||
insertStatement.setString(2, serviceId.toString());
|
||||
insertStatement.execute();
|
||||
}
|
||||
}
|
||||
|
||||
statement.executeUpdate("""
|
||||
CREATE TABLE identity2 (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
address TEXT UNIQUE NOT NULL,
|
||||
identity_key BLOB NOT NULL,
|
||||
added_timestamp INTEGER NOT NULL,
|
||||
trust_level INTEGER NOT NULL
|
||||
) STRICT;
|
||||
INSERT INTO identity2 (_id, address, identity_key, added_timestamp, trust_level)
|
||||
SELECT i._id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = i.uuid) address, i.identity_key, i.added_timestamp, i.trust_level
|
||||
FROM identity i
|
||||
WHERE address IS NOT NULL;
|
||||
DROP TABLE identity;
|
||||
ALTER TABLE identity2 RENAME TO identity;
|
||||
|
||||
CREATE TABLE message_send_log2 (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
content_id INTEGER NOT NULL REFERENCES message_send_log_content (_id) ON DELETE CASCADE,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL
|
||||
) STRICT;
|
||||
INSERT INTO message_send_log2 (_id, content_id, address, device_id)
|
||||
SELECT m._id, m.content_id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = m.uuid) address, m.device_id
|
||||
FROM message_send_log m
|
||||
WHERE address IS NOT NULL;
|
||||
DROP INDEX msl_recipient_index;
|
||||
DROP INDEX msl_content_index;
|
||||
DROP TABLE message_send_log;
|
||||
ALTER TABLE message_send_log2 RENAME TO message_send_log;
|
||||
CREATE INDEX msl_recipient_index ON message_send_log (address, device_id, content_id);
|
||||
CREATE INDEX msl_content_index ON message_send_log (content_id);
|
||||
|
||||
CREATE TABLE sender_key2 (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
distribution_id BLOB NOT NULL,
|
||||
record BLOB NOT NULL,
|
||||
created_timestamp INTEGER NOT NULL,
|
||||
UNIQUE(address, device_id, distribution_id)
|
||||
) STRICT;
|
||||
INSERT INTO sender_key2 (_id, address, device_id, distribution_id, record, created_timestamp)
|
||||
SELECT s._id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = s.uuid) address, s.device_id, s.distribution_id, s.record, s.created_timestamp
|
||||
FROM sender_key s
|
||||
WHERE address IS NOT NULL;
|
||||
DROP TABLE sender_key;
|
||||
ALTER TABLE sender_key2 RENAME TO sender_key;
|
||||
|
||||
CREATE TABLE sender_key_shared2 (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
distribution_id BLOB NOT NULL,
|
||||
timestamp INTEGER NOT NULL,
|
||||
UNIQUE(address, device_id, distribution_id)
|
||||
) STRICT;
|
||||
INSERT INTO sender_key_shared2 (_id, address, device_id, distribution_id, timestamp)
|
||||
SELECT s._id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = s.uuid) address, s.device_id, s.distribution_id, s.timestamp
|
||||
FROM sender_key_shared s
|
||||
WHERE address IS NOT NULL;
|
||||
DROP TABLE sender_key_shared;
|
||||
ALTER TABLE sender_key_shared2 RENAME TO sender_key_shared;
|
||||
|
||||
CREATE TABLE session2 (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
account_id_type INTEGER NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
record BLOB NOT NULL,
|
||||
UNIQUE(account_id_type, address, device_id)
|
||||
) STRICT;
|
||||
INSERT INTO session2 (_id, account_id_type, address, device_id, record)
|
||||
SELECT s._id, s.account_id_type, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = s.uuid) address, s.device_id, s.record
|
||||
FROM session s
|
||||
WHERE address IS NOT NULL;
|
||||
DROP TABLE session;
|
||||
ALTER TABLE session2 RENAME TO session;
|
||||
|
||||
DROP TABLE tmp_mapping_table;
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1841,8 +1841,7 @@ public class SignalAccount implements Closeable {
|
|||
|
||||
public SignalIdentityKeyStore getIdentityKeyStore() {
|
||||
return getOrCreate(() -> identityKeyStore,
|
||||
() -> identityKeyStore = new SignalIdentityKeyStore(getRecipientResolver(),
|
||||
() -> identityKeyPair,
|
||||
() -> identityKeyStore = new SignalIdentityKeyStore(() -> identityKeyPair,
|
||||
localRegistrationId,
|
||||
SignalAccount.this.getIdentityKeyStore()));
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class Utils {
|
|||
return node;
|
||||
}
|
||||
|
||||
public static RecipientAddress getRecipientAddressFromIdentifier(final String identifier) {
|
||||
public static RecipientAddress getRecipientAddressFromLegacyIdentifier(final String identifier) {
|
||||
if (UuidUtil.isUuid(identifier)) {
|
||||
return new RecipientAddress(ServiceId.parseOrThrow(identifier));
|
||||
} else {
|
||||
|
|
|
@ -6,26 +6,26 @@ import org.whispersystems.signalservice.api.push.ServiceId;
|
|||
|
||||
public class IdentityInfo {
|
||||
|
||||
private final String name;
|
||||
private final String address;
|
||||
private final IdentityKey identityKey;
|
||||
private final TrustLevel trustLevel;
|
||||
private final long addedTimestamp;
|
||||
|
||||
IdentityInfo(
|
||||
final String name, IdentityKey identityKey, TrustLevel trustLevel, long addedTimestamp
|
||||
final String address, IdentityKey identityKey, TrustLevel trustLevel, long addedTimestamp
|
||||
) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.identityKey = identityKey;
|
||||
this.trustLevel = trustLevel;
|
||||
this.addedTimestamp = addedTimestamp;
|
||||
}
|
||||
|
||||
public ServiceId getServiceId() {
|
||||
return ServiceId.parseOrThrow(name);
|
||||
return ServiceId.parseOrThrow(address);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public IdentityKey getIdentityKey() {
|
||||
|
|
|
@ -37,7 +37,7 @@ public class IdentityKeyStore {
|
|||
statement.executeUpdate("""
|
||||
CREATE TABLE identity (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
uuid BLOB UNIQUE NOT NULL,
|
||||
address TEXT UNIQUE NOT NULL,
|
||||
identity_key BLOB NOT NULL,
|
||||
added_timestamp INTEGER NOT NULL,
|
||||
trust_level INTEGER NOT NULL
|
||||
|
@ -56,18 +56,22 @@ public class IdentityKeyStore {
|
|||
}
|
||||
|
||||
public boolean saveIdentity(final ServiceId serviceId, final IdentityKey identityKey) {
|
||||
return saveIdentity(serviceId.toString(), identityKey);
|
||||
}
|
||||
|
||||
boolean saveIdentity(final String address, final IdentityKey identityKey) {
|
||||
if (isRetryingDecryption) {
|
||||
return false;
|
||||
}
|
||||
try (final var connection = database.getConnection()) {
|
||||
final var identityInfo = loadIdentity(connection, serviceId);
|
||||
final var identityInfo = loadIdentity(connection, address);
|
||||
if (identityInfo != null && identityInfo.getIdentityKey().equals(identityKey)) {
|
||||
// Identity already exists, not updating the trust level
|
||||
logger.trace("Not storing new identity for recipient {}, identity already stored", serviceId);
|
||||
logger.trace("Not storing new identity for recipient {}, identity already stored", address);
|
||||
return false;
|
||||
}
|
||||
|
||||
saveNewIdentity(connection, serviceId, identityKey, identityInfo == null);
|
||||
saveNewIdentity(connection, address, identityKey, identityInfo == null);
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed update identity store", e);
|
||||
|
@ -80,7 +84,8 @@ public class IdentityKeyStore {
|
|||
|
||||
public boolean setIdentityTrustLevel(ServiceId serviceId, IdentityKey identityKey, TrustLevel trustLevel) {
|
||||
try (final var connection = database.getConnection()) {
|
||||
final var identityInfo = loadIdentity(connection, serviceId);
|
||||
final var address = serviceId.toString();
|
||||
final var identityInfo = loadIdentity(connection, address);
|
||||
if (identityInfo == null) {
|
||||
logger.debug("Not updating trust level for recipient {}, identity not found", serviceId);
|
||||
return false;
|
||||
|
@ -95,7 +100,7 @@ public class IdentityKeyStore {
|
|||
}
|
||||
|
||||
logger.debug("Updating trust level for recipient {} with trust {}", serviceId, trustLevel);
|
||||
final var newIdentityInfo = new IdentityInfo(serviceId,
|
||||
final var newIdentityInfo = new IdentityInfo(address,
|
||||
identityKey,
|
||||
trustLevel,
|
||||
identityInfo.getDateAddedTimestamp());
|
||||
|
@ -107,31 +112,35 @@ public class IdentityKeyStore {
|
|||
}
|
||||
|
||||
public boolean isTrustedIdentity(ServiceId serviceId, IdentityKey identityKey, Direction direction) {
|
||||
return isTrustedIdentity(serviceId.toString(), identityKey, direction);
|
||||
}
|
||||
|
||||
public boolean isTrustedIdentity(String address, IdentityKey identityKey, Direction direction) {
|
||||
if (trustNewIdentity == TrustNewIdentity.ALWAYS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try (final var connection = database.getConnection()) {
|
||||
// TODO implement possibility for different handling of incoming/outgoing trust decisions
|
||||
var identityInfo = loadIdentity(connection, serviceId);
|
||||
var identityInfo = loadIdentity(connection, address);
|
||||
if (identityInfo == null) {
|
||||
logger.debug("Initial identity found for {}, saving.", serviceId);
|
||||
saveNewIdentity(connection, serviceId, identityKey, true);
|
||||
identityInfo = loadIdentity(connection, serviceId);
|
||||
logger.debug("Initial identity found for {}, saving.", address);
|
||||
saveNewIdentity(connection, address, identityKey, true);
|
||||
identityInfo = loadIdentity(connection, address);
|
||||
} else if (!identityInfo.getIdentityKey().equals(identityKey)) {
|
||||
// Identity found, but different
|
||||
if (direction == Direction.SENDING) {
|
||||
logger.debug("Changed identity found for {}, saving.", serviceId);
|
||||
saveNewIdentity(connection, serviceId, identityKey, false);
|
||||
identityInfo = loadIdentity(connection, serviceId);
|
||||
logger.debug("Changed identity found for {}, saving.", address);
|
||||
saveNewIdentity(connection, address, identityKey, false);
|
||||
identityInfo = loadIdentity(connection, address);
|
||||
} else {
|
||||
logger.trace("Trusting identity for {} for {}: {}", serviceId, direction, false);
|
||||
logger.trace("Trusting identity for {} for {}: {}", address, direction, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final var isTrusted = identityInfo != null && identityInfo.isTrusted();
|
||||
logger.trace("Trusting identity for {} for {}: {}", serviceId, direction, isTrusted);
|
||||
logger.trace("Trusting identity for {} for {}: {}", address, direction, isTrusted);
|
||||
return isTrusted;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed read from identity store", e);
|
||||
|
@ -139,8 +148,12 @@ public class IdentityKeyStore {
|
|||
}
|
||||
|
||||
public IdentityInfo getIdentityInfo(ServiceId serviceId) {
|
||||
return getIdentityInfo(serviceId.toString());
|
||||
}
|
||||
|
||||
public IdentityInfo getIdentityInfo(String address) {
|
||||
try (final var connection = database.getConnection()) {
|
||||
return loadIdentity(connection, serviceId);
|
||||
return loadIdentity(connection, address);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed read from identity store", e);
|
||||
}
|
||||
|
@ -150,7 +163,7 @@ public class IdentityKeyStore {
|
|||
try (final var connection = database.getConnection()) {
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT i.uuid, i.identity_key, i.added_timestamp, i.trust_level
|
||||
SELECT i.address, i.identity_key, i.added_timestamp, i.trust_level
|
||||
FROM %s AS i
|
||||
"""
|
||||
).formatted(TABLE_IDENTITY);
|
||||
|
@ -166,7 +179,7 @@ public class IdentityKeyStore {
|
|||
|
||||
public void deleteIdentity(final ServiceId serviceId) {
|
||||
try (final var connection = database.getConnection()) {
|
||||
deleteIdentity(connection, serviceId);
|
||||
deleteIdentity(connection, serviceId.toString());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed update identity store", e);
|
||||
}
|
||||
|
@ -188,34 +201,37 @@ public class IdentityKeyStore {
|
|||
}
|
||||
|
||||
private IdentityInfo loadIdentity(
|
||||
final Connection connection, final ServiceId serviceId
|
||||
final Connection connection, final String address
|
||||
) throws SQLException {
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT i.uuid, i.identity_key, i.added_timestamp, i.trust_level
|
||||
SELECT i.address, i.identity_key, i.added_timestamp, i.trust_level
|
||||
FROM %s AS i
|
||||
WHERE i.uuid = ?
|
||||
WHERE i.address = ?
|
||||
"""
|
||||
).formatted(TABLE_IDENTITY);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, serviceId.toByteArray());
|
||||
statement.setString(1, address);
|
||||
return Utils.executeQueryForOptional(statement, this::getIdentityInfoFromResultSet).orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveNewIdentity(
|
||||
final Connection connection,
|
||||
final ServiceId serviceId,
|
||||
final String address,
|
||||
final IdentityKey identityKey,
|
||||
final boolean firstIdentity
|
||||
) throws SQLException {
|
||||
final var trustLevel = trustNewIdentity == TrustNewIdentity.ALWAYS || (
|
||||
trustNewIdentity == TrustNewIdentity.ON_FIRST_USE && firstIdentity
|
||||
) ? TrustLevel.TRUSTED_UNVERIFIED : TrustLevel.UNTRUSTED;
|
||||
logger.debug("Storing new identity for recipient {} with trust {}", serviceId, trustLevel);
|
||||
final var newIdentityInfo = new IdentityInfo(serviceId, identityKey, trustLevel, System.currentTimeMillis());
|
||||
logger.debug("Storing new identity for recipient {} with trust {}", address, trustLevel);
|
||||
final var newIdentityInfo = new IdentityInfo(address, identityKey, trustLevel, System.currentTimeMillis());
|
||||
storeIdentity(connection, newIdentityInfo);
|
||||
identityChanges.onNext(serviceId);
|
||||
final var serviceId = ServiceId.parseOrNull(address);
|
||||
if (serviceId != null) {
|
||||
identityChanges.onNext(serviceId);
|
||||
}
|
||||
}
|
||||
|
||||
private void storeIdentity(final Connection connection, final IdentityInfo identityInfo) throws SQLException {
|
||||
|
@ -225,12 +241,12 @@ public class IdentityKeyStore {
|
|||
identityInfo.getDateAddedTimestamp());
|
||||
final var sql = (
|
||||
"""
|
||||
INSERT OR REPLACE INTO %s (uuid, identity_key, added_timestamp, trust_level)
|
||||
INSERT OR REPLACE INTO %s (address, identity_key, added_timestamp, trust_level)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
).formatted(TABLE_IDENTITY);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, identityInfo.getServiceId().toByteArray());
|
||||
statement.setString(1, identityInfo.getAddress());
|
||||
statement.setBytes(2, identityInfo.getIdentityKey().serialize());
|
||||
statement.setLong(3, identityInfo.getDateAddedTimestamp());
|
||||
statement.setInt(4, identityInfo.getTrustLevel().ordinal());
|
||||
|
@ -238,27 +254,27 @@ public class IdentityKeyStore {
|
|||
}
|
||||
}
|
||||
|
||||
private void deleteIdentity(final Connection connection, final ServiceId serviceId) throws SQLException {
|
||||
private void deleteIdentity(final Connection connection, final String address) throws SQLException {
|
||||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS i
|
||||
WHERE i.uuid = ?
|
||||
WHERE i.address = ?
|
||||
"""
|
||||
).formatted(TABLE_IDENTITY);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, serviceId.toByteArray());
|
||||
statement.setString(1, address);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private IdentityInfo getIdentityInfoFromResultSet(ResultSet resultSet) throws SQLException {
|
||||
try {
|
||||
final var serviceId = ServiceId.parseOrThrow(resultSet.getBytes("uuid"));
|
||||
final var address = resultSet.getString("address");
|
||||
final var id = new IdentityKey(resultSet.getBytes("identity_key"));
|
||||
final var trustLevel = TrustLevel.fromInt(resultSet.getInt("trust_level"));
|
||||
final var added = resultSet.getLong("added_timestamp");
|
||||
|
||||
return new IdentityInfo(serviceId, id, trustLevel, added);
|
||||
return new IdentityInfo(address, id, trustLevel, added);
|
||||
} catch (InvalidKeyException e) {
|
||||
logger.warn("Failed to load identity key, resetting: {}", e.getMessage());
|
||||
return null;
|
||||
|
|
|
@ -84,7 +84,7 @@ public class LegacyIdentityKeyStore {
|
|||
var added = storage.addedTimestamp();
|
||||
|
||||
final var serviceId = address.serviceId().get();
|
||||
return new IdentityInfo(serviceId, id, trustLevel, added);
|
||||
return new IdentityInfo(serviceId.toString(), id, trustLevel, added);
|
||||
} catch (IOException | InvalidKeyException e) {
|
||||
logger.warn("Failed to load identity key: {}", e.getMessage());
|
||||
return null;
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package org.asamk.signal.manager.storage.identities;
|
||||
|
||||
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair;
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -15,7 +13,6 @@ public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.sta
|
|||
private final IdentityKeyStore identityKeyStore;
|
||||
|
||||
public SignalIdentityKeyStore(
|
||||
final RecipientResolver resolver,
|
||||
final Supplier<IdentityKeyPair> identityKeyPairSupplier,
|
||||
final int localRegistrationId,
|
||||
final IdentityKeyStore identityKeyStore
|
||||
|
@ -37,22 +34,17 @@ public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.sta
|
|||
|
||||
@Override
|
||||
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
|
||||
final var serviceId = ServiceId.parseOrThrow(address.getName());
|
||||
|
||||
return identityKeyStore.saveIdentity(serviceId, identityKey);
|
||||
return identityKeyStore.saveIdentity(address.getName(), identityKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
|
||||
final var serviceId = ServiceId.parseOrThrow(address.getName());
|
||||
|
||||
return identityKeyStore.isTrustedIdentity(serviceId, identityKey, direction);
|
||||
return identityKeyStore.isTrustedIdentity(address.getName(), identityKey, direction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey getIdentity(SignalProtocolAddress address) {
|
||||
final var serviceId = ServiceId.parseOrThrow(address.getName());
|
||||
final var identityInfo = identityKeyStore.getIdentityInfo(serviceId);
|
||||
final var identityInfo = identityKeyStore.getIdentityInfo(address.getName());
|
||||
return identityInfo == null ? null : identityInfo.getIdentityKey();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public class LegacyJsonIdentityKeyStore {
|
|||
? UuidUtil.parseOrNull(trustedKey.get("uuid").asText())
|
||||
: null;
|
||||
final var address = uuid == null
|
||||
? Utils.getRecipientAddressFromIdentifier(trustedKeyName)
|
||||
? Utils.getRecipientAddressFromLegacyIdentifier(trustedKeyName)
|
||||
: new RecipientAddress(ACI.from(uuid), trustedKeyName);
|
||||
try {
|
||||
var id = new IdentityKey(Base64.getDecoder().decode(trustedKey.get("identityKey").asText()), 0);
|
||||
|
|
|
@ -47,7 +47,7 @@ public class LegacyJsonSessionStore {
|
|||
|
||||
var uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null;
|
||||
final var address = uuid == null
|
||||
? Utils.getRecipientAddressFromIdentifier(sessionName)
|
||||
? Utils.getRecipientAddressFromLegacyIdentifier(sessionName)
|
||||
: new RecipientAddress(ACI.from(uuid), sessionName);
|
||||
final var deviceId = session.get("deviceId").asInt();
|
||||
final var record = Base64.getDecoder().decode(session.get("record").asText());
|
||||
|
|
|
@ -151,8 +151,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
|
||||
@Override
|
||||
public RecipientId resolveRecipient(final String identifier) {
|
||||
if (UuidUtil.isUuid(identifier)) {
|
||||
return resolveRecipient(ServiceId.parseOrThrow(identifier));
|
||||
final var serviceId = ServiceId.parseOrNull(identifier);
|
||||
if (serviceId != null) {
|
||||
return resolveRecipient(serviceId);
|
||||
} else {
|
||||
return resolveRecipientByNumber(identifier);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public class MessageSendLogStore implements AutoCloseable {
|
|||
CREATE TABLE message_send_log (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
content_id INTEGER NOT NULL REFERENCES message_send_log_content (_id) ON DELETE CASCADE,
|
||||
uuid BLOB NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL
|
||||
) STRICT;
|
||||
CREATE TABLE message_send_log_content (
|
||||
|
@ -79,7 +79,7 @@ public class MessageSendLogStore implements AutoCloseable {
|
|||
urgent INTEGER NOT NULL
|
||||
) STRICT;
|
||||
CREATE INDEX mslc_timestamp_index ON message_send_log_content (timestamp);
|
||||
CREATE INDEX msl_recipient_index ON message_send_log (uuid, device_id, content_id);
|
||||
CREATE INDEX msl_recipient_index ON message_send_log (address, device_id, content_id);
|
||||
CREATE INDEX msl_content_index ON message_send_log (content_id);
|
||||
""");
|
||||
}
|
||||
|
@ -92,13 +92,13 @@ public class MessageSendLogStore implements AutoCloseable {
|
|||
SELECT group_id, content, content_hint, urgent
|
||||
FROM %s l
|
||||
INNER JOIN %s lc ON l.content_id = lc._id
|
||||
WHERE l.uuid = ? AND l.device_id = ? AND lc.timestamp = ?
|
||||
WHERE l.address = ? AND l.device_id = ? AND lc.timestamp = ?
|
||||
""".formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT);
|
||||
try (final var connection = database.getConnection()) {
|
||||
deleteOutdatedEntries(connection);
|
||||
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, serviceId.toByteArray());
|
||||
statement.setString(1, serviceId.toString());
|
||||
statement.setInt(2, deviceId);
|
||||
statement.setLong(3, timestamp);
|
||||
try (var result = Utils.executeQueryForStream(statement, this::getMessageSendLogEntryFromResultSet)) {
|
||||
|
@ -202,13 +202,13 @@ public class MessageSendLogStore implements AutoCloseable {
|
|||
public void deleteEntryForRecipientNonGroup(long sentTimestamp, ServiceId serviceId) {
|
||||
final var sql = """
|
||||
DELETE FROM %s AS lc
|
||||
WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.uuid = ?)
|
||||
WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.address = ?)
|
||||
""".formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, TABLE_MESSAGE_SEND_LOG);
|
||||
try (final var connection = database.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setLong(1, sentTimestamp);
|
||||
statement.setBytes(2, serviceId.toByteArray());
|
||||
statement.setString(2, serviceId.toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
|
@ -226,14 +226,14 @@ public class MessageSendLogStore implements AutoCloseable {
|
|||
public void deleteEntriesForRecipient(List<Long> sentTimestamps, ServiceId serviceId, int deviceId) {
|
||||
final var sql = """
|
||||
DELETE FROM %s AS l
|
||||
WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.uuid = ? AND l.device_id = ?
|
||||
WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.address = ? AND l.device_id = ?
|
||||
""".formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT);
|
||||
try (final var connection = database.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
for (final var sentTimestamp : sentTimestamps) {
|
||||
statement.setLong(1, sentTimestamp);
|
||||
statement.setBytes(2, serviceId.toByteArray());
|
||||
statement.setString(2, serviceId.toString());
|
||||
statement.setInt(3, deviceId);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
@ -342,13 +342,13 @@ public class MessageSendLogStore implements AutoCloseable {
|
|||
final long contentId, final List<RecipientDevices> recipientDevices, final Connection connection
|
||||
) throws SQLException {
|
||||
final var sql = """
|
||||
INSERT INTO %s (uuid, device_id, content_id)
|
||||
INSERT INTO %s (address, device_id, content_id)
|
||||
VALUES (?,?,?)
|
||||
""".formatted(TABLE_MESSAGE_SEND_LOG);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
for (final var recipientDevice : recipientDevices) {
|
||||
for (final var deviceId : recipientDevice.deviceIds()) {
|
||||
statement.setBytes(1, recipientDevice.serviceId().toByteArray());
|
||||
statement.setString(1, recipientDevice.serviceId().toString());
|
||||
statement.setInt(2, deviceId);
|
||||
statement.setLong(3, contentId);
|
||||
statement.executeUpdate();
|
||||
|
|
|
@ -41,7 +41,9 @@ public class LegacySenderKeyRecordStore {
|
|||
if (record == null || serviceId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new Pair<>(new SenderKeyRecordStore.Key(serviceId.get(), key.deviceId, key.distributionId), record);
|
||||
return new Pair<>(new SenderKeyRecordStore.Key(serviceId.get().toString(),
|
||||
key.deviceId,
|
||||
key.distributionId), record);
|
||||
}).filter(Objects::nonNull).toList();
|
||||
|
||||
senderKeyStore.addLegacySenderKeys(senderKeys);
|
||||
|
|
|
@ -40,7 +40,7 @@ public class LegacySenderKeySharedStore {
|
|||
if (serviceId.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
final var entry = new SenderKeySharedEntry(serviceId.get(), senderKey.deviceId);
|
||||
final var entry = new SenderKeySharedEntry(serviceId.get().toString(), senderKey.deviceId);
|
||||
final var distributionId = DistributionId.from(senderKey.distributionId);
|
||||
var entries = sharedSenderKeys.get(distributionId);
|
||||
if (entries == null) {
|
||||
|
|
|
@ -31,12 +31,12 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
statement.executeUpdate("""
|
||||
CREATE TABLE sender_key (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
uuid BLOB NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
distribution_id BLOB NOT NULL,
|
||||
record BLOB NOT NULL,
|
||||
created_timestamp INTEGER NOT NULL,
|
||||
UNIQUE(uuid, device_id, distribution_id)
|
||||
UNIQUE(address, device_id, distribution_id)
|
||||
) STRICT;
|
||||
""");
|
||||
}
|
||||
|
@ -75,12 +75,12 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
"""
|
||||
SELECT s.created_timestamp
|
||||
FROM %s AS s
|
||||
WHERE s.uuid = ? AND s.device_id = ? AND s.distribution_id = ?
|
||||
WHERE s.address = ? AND s.device_id = ? AND s.distribution_id = ?
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY);
|
||||
try (final var connection = database.getConnection()) {
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, selfServiceId.toByteArray());
|
||||
statement.setString(1, selfServiceId.toString());
|
||||
statement.setInt(2, selfDeviceId);
|
||||
statement.setBytes(3, UuidUtil.toByteArray(distributionId));
|
||||
return Utils.executeQueryForOptional(statement, res -> res.getLong("created_timestamp")).orElse(-1L);
|
||||
|
@ -94,12 +94,12 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS s
|
||||
WHERE s.uuid = ? AND s.distribution_id = ?
|
||||
WHERE s.address = ? AND s.distribution_id = ?
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY);
|
||||
try (final var connection = database.getConnection()) {
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, serviceId.toByteArray());
|
||||
statement.setString(1, serviceId.toString());
|
||||
statement.setBytes(2, UuidUtil.toByteArray(distributionId));
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
@ -145,8 +145,7 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
}
|
||||
|
||||
private Key getKey(final SignalProtocolAddress address, final UUID distributionId) {
|
||||
final var serviceId = ServiceId.parseOrThrow(address.getName());
|
||||
return new Key(serviceId, address.getDeviceId(), distributionId);
|
||||
return new Key(address.getName(), address.getDeviceId(), distributionId);
|
||||
}
|
||||
|
||||
private SenderKeyRecord loadSenderKey(final Connection connection, final Key key) throws SQLException {
|
||||
|
@ -154,11 +153,11 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
"""
|
||||
SELECT s.record
|
||||
FROM %s AS s
|
||||
WHERE s.uuid = ? AND s.device_id = ? AND s.distribution_id = ?
|
||||
WHERE s.address = ? AND s.device_id = ? AND s.distribution_id = ?
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, key.serviceId().toByteArray());
|
||||
statement.setString(1, key.address());
|
||||
statement.setInt(2, key.deviceId());
|
||||
statement.setBytes(3, UuidUtil.toByteArray(key.distributionId()));
|
||||
return Utils.executeQueryForOptional(statement, this::getSenderKeyRecordFromResultSet).orElse(null);
|
||||
|
@ -171,11 +170,11 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
final var sqlUpdate = """
|
||||
UPDATE %s
|
||||
SET record = ?
|
||||
WHERE uuid = ? AND device_id = ? and distribution_id = ?
|
||||
WHERE address = ? AND device_id = ? and distribution_id = ?
|
||||
""".formatted(TABLE_SENDER_KEY);
|
||||
try (final var statement = connection.prepareStatement(sqlUpdate)) {
|
||||
statement.setBytes(1, senderKeyRecord.serialize());
|
||||
statement.setBytes(2, key.serviceId().toByteArray());
|
||||
statement.setString(2, key.address());
|
||||
statement.setLong(3, key.deviceId());
|
||||
statement.setBytes(4, UuidUtil.toByteArray(key.distributionId()));
|
||||
final var rows = statement.executeUpdate();
|
||||
|
@ -187,12 +186,12 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
// Record doesn't exist yet, creating a new one
|
||||
final var sqlInsert = (
|
||||
"""
|
||||
INSERT OR REPLACE INTO %s (uuid, device_id, distribution_id, record, created_timestamp)
|
||||
INSERT OR REPLACE INTO %s (address, device_id, distribution_id, record, created_timestamp)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY);
|
||||
try (final var statement = connection.prepareStatement(sqlInsert)) {
|
||||
statement.setBytes(1, key.serviceId().toByteArray());
|
||||
statement.setString(1, key.address());
|
||||
statement.setInt(2, key.deviceId());
|
||||
statement.setBytes(3, UuidUtil.toByteArray(key.distributionId()));
|
||||
statement.setBytes(4, senderKeyRecord.serialize());
|
||||
|
@ -205,11 +204,11 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS s
|
||||
WHERE s.uuid = ?
|
||||
WHERE s.address = ?
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, serviceId.toByteArray());
|
||||
statement.setString(1, serviceId.toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -225,5 +224,5 @@ public class SenderKeyRecordStore implements SenderKeyStore {
|
|||
}
|
||||
}
|
||||
|
||||
record Key(ServiceId serviceId, int deviceId, UUID distributionId) {}
|
||||
record Key(String address, int deviceId, UUID distributionId) {}
|
||||
}
|
||||
|
|
|
@ -30,11 +30,11 @@ public class SenderKeySharedStore {
|
|||
statement.executeUpdate("""
|
||||
CREATE TABLE sender_key_shared (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
uuid BLOB NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
distribution_id BLOB NOT NULL,
|
||||
timestamp INTEGER NOT NULL,
|
||||
UNIQUE(uuid, device_id, distribution_id)
|
||||
UNIQUE(address, device_id, distribution_id)
|
||||
) STRICT;
|
||||
""");
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class SenderKeySharedStore {
|
|||
try (final var connection = database.getConnection()) {
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT s.uuid, s.device_id
|
||||
SELECT s.address, s.device_id
|
||||
FROM %s AS s
|
||||
WHERE s.distribution_id = ?
|
||||
"""
|
||||
|
@ -56,7 +56,7 @@ public class SenderKeySharedStore {
|
|||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, UuidUtil.toByteArray(distributionId.asUuid()));
|
||||
return Utils.executeQueryForStream(statement, this::getSenderKeySharedEntryFromResultSet)
|
||||
.map(k -> k.serviceId.toProtocolAddress(k.deviceId()))
|
||||
.map(k -> new SignalProtocolAddress(k.address, k.deviceId()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
|
@ -68,7 +68,7 @@ public class SenderKeySharedStore {
|
|||
final DistributionId distributionId, final Collection<SignalProtocolAddress> addresses
|
||||
) {
|
||||
final var newEntries = addresses.stream()
|
||||
.map(a -> new SenderKeySharedEntry(ServiceId.parseOrThrow(a.getName()), a.getDeviceId()))
|
||||
.map(a -> new SenderKeySharedEntry(a.getName(), a.getDeviceId()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
try (final var connection = database.getConnection()) {
|
||||
|
@ -82,8 +82,7 @@ public class SenderKeySharedStore {
|
|||
|
||||
public void clearSenderKeySharedWith(final Collection<SignalProtocolAddress> addresses) {
|
||||
final var entriesToDelete = addresses.stream()
|
||||
.filter(a -> UuidUtil.isUuid(a.getName()))
|
||||
.map(a -> new SenderKeySharedEntry(ServiceId.parseOrThrow(a.getName()), a.getDeviceId()))
|
||||
.map(a -> new SenderKeySharedEntry(a.getName(), a.getDeviceId()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
try (final var connection = database.getConnection()) {
|
||||
|
@ -91,12 +90,12 @@ public class SenderKeySharedStore {
|
|||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS s
|
||||
WHERE uuid = ? AND device_id = ?
|
||||
WHERE address = ? AND device_id = ?
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY_SHARED);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
for (final var entry : entriesToDelete) {
|
||||
statement.setBytes(1, entry.serviceId().toByteArray());
|
||||
statement.setString(1, entry.address());
|
||||
statement.setInt(2, entry.deviceId());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
@ -127,11 +126,11 @@ public class SenderKeySharedStore {
|
|||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS s
|
||||
WHERE uuid = ?
|
||||
WHERE address = ?
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY_SHARED);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, serviceId.toByteArray());
|
||||
statement.setString(1, serviceId.toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
|
@ -146,11 +145,11 @@ public class SenderKeySharedStore {
|
|||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS s
|
||||
WHERE uuid = ? AND device_id = ? AND distribution_id = ?
|
||||
WHERE address = ? AND device_id = ? AND distribution_id = ?
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY_SHARED);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBytes(1, serviceId.toByteArray());
|
||||
statement.setString(1, serviceId.toString());
|
||||
statement.setInt(2, deviceId);
|
||||
statement.setBytes(3, UuidUtil.toByteArray(distributionId.asUuid()));
|
||||
statement.executeUpdate();
|
||||
|
@ -197,13 +196,13 @@ public class SenderKeySharedStore {
|
|||
) throws SQLException {
|
||||
final var sql = (
|
||||
"""
|
||||
INSERT OR REPLACE INTO %s (uuid, device_id, distribution_id, timestamp)
|
||||
INSERT OR REPLACE INTO %s (address, device_id, distribution_id, timestamp)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
).formatted(TABLE_SENDER_KEY_SHARED);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
for (final var entry : newEntries) {
|
||||
statement.setBytes(1, entry.serviceId().toByteArray());
|
||||
statement.setString(1, entry.toString());
|
||||
statement.setInt(2, entry.deviceId());
|
||||
statement.setBytes(3, UuidUtil.toByteArray(distributionId.asUuid()));
|
||||
statement.setLong(4, System.currentTimeMillis());
|
||||
|
@ -213,10 +212,10 @@ public class SenderKeySharedStore {
|
|||
}
|
||||
|
||||
private SenderKeySharedEntry getSenderKeySharedEntryFromResultSet(ResultSet resultSet) throws SQLException {
|
||||
final var serviceId = ServiceId.parseOrThrow(resultSet.getBytes("uuid"));
|
||||
final var address = resultSet.getString("address");
|
||||
final var deviceId = resultSet.getInt("device_id");
|
||||
return new SenderKeySharedEntry(serviceId, deviceId);
|
||||
return new SenderKeySharedEntry(address, deviceId);
|
||||
}
|
||||
|
||||
record SenderKeySharedEntry(ServiceId serviceId, int deviceId) {}
|
||||
record SenderKeySharedEntry(String address, int deviceId) {}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public class LegacySessionStore {
|
|||
if (record == null || serviceId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new Pair<>(new SessionStore.Key(serviceId.get(), key.deviceId()), record);
|
||||
return new Pair<>(new SessionStore.Key(serviceId.get().toString(), key.deviceId()), record);
|
||||
}).filter(Objects::nonNull).toList();
|
||||
sessionStore.addLegacySessions(sessions);
|
||||
deleteAllSessions(sessionsPath);
|
||||
|
|
|
@ -6,15 +6,12 @@ import org.asamk.signal.manager.storage.Utils;
|
|||
import org.signal.libsignal.protocol.NoSessionException;
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress;
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey;
|
||||
import org.signal.libsignal.protocol.message.CiphertextMessage;
|
||||
import org.signal.libsignal.protocol.state.SessionRecord;
|
||||
import org.signal.libsignal.protocol.util.Hex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.signalservice.api.SignalServiceSessionStore;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
|
@ -45,10 +42,10 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
CREATE TABLE session (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
account_id_type INTEGER NOT NULL,
|
||||
uuid BLOB NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
record BLOB NOT NULL,
|
||||
UNIQUE(account_id_type, uuid, device_id)
|
||||
UNIQUE(account_id_type, address, device_id)
|
||||
) STRICT;
|
||||
""");
|
||||
}
|
||||
|
@ -107,13 +104,13 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
"""
|
||||
SELECT s.device_id
|
||||
FROM %s AS s
|
||||
WHERE s.account_id_type = ? AND s.uuid = ? AND s.device_id != 1
|
||||
WHERE s.account_id_type = ? AND s.address = ? AND s.device_id != 1
|
||||
"""
|
||||
).formatted(TABLE_SESSION);
|
||||
try (final var connection = database.getConnection()) {
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setInt(1, accountIdType);
|
||||
statement.setBytes(2, serviceId.toByteArray());
|
||||
statement.setString(2, serviceId.toString());
|
||||
return Utils.executeQueryForStream(statement, res -> res.getInt("device_id")).toList();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
|
@ -122,7 +119,7 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
}
|
||||
|
||||
public boolean isCurrentRatchetKey(ServiceId serviceId, int deviceId, ECPublicKey ratchetKey) {
|
||||
final var key = new Key(serviceId, deviceId);
|
||||
final var key = new Key(serviceId.toString(), deviceId);
|
||||
|
||||
try (final var connection = database.getConnection()) {
|
||||
final var session = loadSession(connection, key);
|
||||
|
@ -177,7 +174,7 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
|
||||
public void deleteAllSessions(ServiceId serviceId) {
|
||||
try (final var connection = database.getConnection()) {
|
||||
deleteAllSessions(connection, serviceId);
|
||||
deleteAllSessions(connection, serviceId.toString());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed update session store", e);
|
||||
}
|
||||
|
@ -185,10 +182,6 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
|
||||
@Override
|
||||
public void archiveSession(final SignalProtocolAddress address) {
|
||||
if (!UuidUtil.isUuid(address.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var key = getKey(address);
|
||||
|
||||
try (final var connection = database.getConnection()) {
|
||||
|
@ -207,15 +200,13 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
@Override
|
||||
public Set<SignalProtocolAddress> getAllAddressesWithActiveSessions(final List<String> addressNames) {
|
||||
final var serviceIdsCommaSeparated = addressNames.stream()
|
||||
.map(ServiceId::parseOrThrow)
|
||||
.map(ServiceId::toByteArray)
|
||||
.map(uuid -> "x'" + Hex.toStringCondensed(uuid) + "'")
|
||||
.map(address -> "'" + address.replaceAll("'", "''") + "'")
|
||||
.collect(Collectors.joining(","));
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT s.uuid, s.device_id, s.record
|
||||
SELECT s.address, s.device_id, s.record
|
||||
FROM %s AS s
|
||||
WHERE s.account_id_type = ? AND s.uuid IN (%s)
|
||||
WHERE s.account_id_type = ? AND s.address IN (%s)
|
||||
"""
|
||||
).formatted(TABLE_SESSION, serviceIdsCommaSeparated);
|
||||
try (final var connection = database.getConnection()) {
|
||||
|
@ -225,7 +216,7 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
res -> new Pair<>(getKeyFromResultSet(res), getSessionRecordFromResultSet(res)))
|
||||
.filter(pair -> isActive(pair.second()))
|
||||
.map(Pair::first)
|
||||
.map(key -> key.serviceId().toProtocolAddress(key.deviceId()))
|
||||
.map(key -> new SignalProtocolAddress(key.address(), key.deviceId()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
|
@ -236,7 +227,7 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
public void archiveAllSessions() {
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT s.uuid, s.device_id, s.record
|
||||
SELECT s.address, s.device_id, s.record
|
||||
FROM %s AS s
|
||||
WHERE s.account_id_type = ?
|
||||
"""
|
||||
|
@ -264,9 +255,9 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
public void archiveSessions(final ServiceId serviceId) {
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT s.uuid, s.device_id, s.record
|
||||
SELECT s.address, s.device_id, s.record
|
||||
FROM %s AS s
|
||||
WHERE s.account_id_type = ? AND s.uuid = ?
|
||||
WHERE s.account_id_type = ? AND s.address = ?
|
||||
"""
|
||||
).formatted(TABLE_SESSION);
|
||||
try (final var connection = database.getConnection()) {
|
||||
|
@ -274,7 +265,7 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
final List<Pair<Key, SessionRecord>> records;
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setInt(1, accountIdType);
|
||||
statement.setBytes(2, serviceId.toByteArray());
|
||||
statement.setString(2, serviceId.toString());
|
||||
records = Utils.executeQueryForStream(statement,
|
||||
res -> new Pair<>(getKeyFromResultSet(res), getSessionRecordFromResultSet(res)))
|
||||
.filter(Objects::nonNull)
|
||||
|
@ -306,8 +297,7 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
}
|
||||
|
||||
private Key getKey(final SignalProtocolAddress address) {
|
||||
final var serviceId = ServiceId.parseOrThrow(address.getName());
|
||||
return new Key(serviceId, address.getDeviceId());
|
||||
return new Key(address.getName(), address.getDeviceId());
|
||||
}
|
||||
|
||||
private SessionRecord loadSession(Connection connection, final Key key) throws SQLException {
|
||||
|
@ -321,21 +311,21 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
"""
|
||||
SELECT s.record
|
||||
FROM %s AS s
|
||||
WHERE s.account_id_type = ? AND s.uuid = ? AND s.device_id = ?
|
||||
WHERE s.account_id_type = ? AND s.address = ? AND s.device_id = ?
|
||||
"""
|
||||
).formatted(TABLE_SESSION);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setInt(1, accountIdType);
|
||||
statement.setBytes(2, key.serviceId().toByteArray());
|
||||
statement.setString(2, key.address());
|
||||
statement.setInt(3, key.deviceId());
|
||||
return Utils.executeQueryForOptional(statement, this::getSessionRecordFromResultSet).orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
private Key getKeyFromResultSet(ResultSet resultSet) throws SQLException {
|
||||
final var serviceId = ServiceId.parseOrThrow(resultSet.getBytes("uuid"));
|
||||
final var address = resultSet.getString("address");
|
||||
final var deviceId = resultSet.getInt("device_id");
|
||||
return new Key(serviceId, deviceId);
|
||||
return new Key(address, deviceId);
|
||||
}
|
||||
|
||||
private SessionRecord getSessionRecordFromResultSet(ResultSet resultSet) throws SQLException {
|
||||
|
@ -356,19 +346,19 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
}
|
||||
|
||||
final var sql = """
|
||||
INSERT OR REPLACE INTO %s (account_id_type, uuid, device_id, record)
|
||||
INSERT OR REPLACE INTO %s (account_id_type, address, device_id, record)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""".formatted(TABLE_SESSION);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setInt(1, accountIdType);
|
||||
statement.setBytes(2, key.serviceId().toByteArray());
|
||||
statement.setString(2, key.address());
|
||||
statement.setInt(3, key.deviceId());
|
||||
statement.setBytes(4, session.serialize());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteAllSessions(final Connection connection, final ServiceId serviceId) throws SQLException {
|
||||
private void deleteAllSessions(final Connection connection, final String address) throws SQLException {
|
||||
synchronized (cachedSessions) {
|
||||
cachedSessions.clear();
|
||||
}
|
||||
|
@ -376,12 +366,12 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS s
|
||||
WHERE s.account_id_type = ? AND s.uuid = ?
|
||||
WHERE s.account_id_type = ? AND s.address = ?
|
||||
"""
|
||||
).formatted(TABLE_SESSION);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setInt(1, accountIdType);
|
||||
statement.setBytes(2, serviceId.toByteArray());
|
||||
statement.setString(2, address);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -394,12 +384,12 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
final var sql = (
|
||||
"""
|
||||
DELETE FROM %s AS s
|
||||
WHERE s.account_id_type = ? AND s.uuid = ? AND s.device_id = ?
|
||||
WHERE s.account_id_type = ? AND s.address = ? AND s.device_id = ?
|
||||
"""
|
||||
).formatted(TABLE_SESSION);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setInt(1, accountIdType);
|
||||
statement.setBytes(2, key.serviceId().toByteArray());
|
||||
statement.setString(2, key.address());
|
||||
statement.setInt(3, key.deviceId());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
@ -409,5 +399,5 @@ public class SessionStore implements SignalServiceSessionStore {
|
|||
return record != null && record.hasSenderChain();
|
||||
}
|
||||
|
||||
record Key(ServiceId serviceId, int deviceId) {}
|
||||
record Key(String address, int deviceId) {}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue