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