diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
index 3c79254d3..2771302a2 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
@@ -150,6 +150,16 @@ public interface DatabaseComponent extends TransactionManager {
boolean containsPendingContact(Transaction txn, PendingContactId p)
throws DbException;
+ /**
+ * Returns true if the database contains keys for communicating with the
+ * given contact over the given transport. Handshake mode and rotation mode
+ * keys are included, whether activated or not.
+ *
+ * Read-only.
+ */
+ boolean containsTransportKeys(Transaction txn, ContactId c, TransportId t)
+ throws DbException;
+
/**
* Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID,
@@ -493,6 +503,16 @@ public interface DatabaseComponent extends TransactionManager {
Collection getTransportKeys(Transaction txn, TransportId t)
throws DbException;
+ /**
+ * Returns the contact IDs and transport IDs for which the DB contains
+ * at least one set of transport keys. Handshake mode and rotation mode
+ * keys are included, whether activated or not.
+ *
+ * Read-only.
+ */
+ Map> getTransportsWithKeys(
+ Transaction txn) throws DbException;
+
/**
* Increments the outgoing stream counter for the given transport keys.
*/
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
index ab291fa30..705913ee7 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
@@ -215,6 +215,16 @@ interface Database {
*/
boolean containsTransport(T txn, TransportId t) throws DbException;
+ /**
+ * Returns true if the database contains keys for communicating with the
+ * given contact over the given transport. Handshake mode and rotation mode
+ * keys are included, whether activated or not.
+ *
+ * Read-only.
+ */
+ boolean containsTransportKeys(T txn, ContactId c, TransportId t)
+ throws DbException;
+
/**
* Returns true if the database contains the given message, the message is
* shared, and the visibility of the message's group to the given contact
@@ -590,6 +600,16 @@ interface Database {
Collection getTransportKeys(T txn, TransportId t)
throws DbException;
+ /**
+ * Returns the contact IDs and transport IDs for which the DB contains
+ * at least one set of transport keys. Handshake mode and rotation mode
+ * keys are included, whether activated or not.
+ *
+ * Read-only.
+ */
+ Map> getTransportsWithKeys(T txn)
+ throws DbException;
+
/**
* Increments the outgoing stream counter for the given transport keys.
*/
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
index 40ba6fa31..b2422f9bc 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
@@ -371,6 +371,13 @@ class DatabaseComponentImpl implements DatabaseComponent {
return db.containsPendingContact(txn, p);
}
+ @Override
+ public boolean containsTransportKeys(Transaction transaction, ContactId c,
+ TransportId t) throws DbException {
+ T txn = unbox(transaction);
+ return db.containsTransportKeys(txn, c, t);
+ }
+
@Override
public void deleteMessage(Transaction transaction, MessageId m)
throws DbException {
@@ -780,6 +787,13 @@ class DatabaseComponentImpl implements DatabaseComponent {
return db.getTransportKeys(txn, t);
}
+ @Override
+ public Map> getTransportsWithKeys(
+ Transaction transaction) throws DbException {
+ T txn = unbox(transaction);
+ return db.getTransportsWithKeys(txn);
+ }
+
@Override
public void incrementStreamCounter(Transaction transaction, TransportId t,
KeySetId k) throws DbException {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index af54d4e9d..224b20bf1 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -1277,6 +1277,29 @@ abstract class JdbcDatabase implements Database {
}
}
+ @Override
+ public boolean containsTransportKeys(Connection txn, ContactId c,
+ TransportId t) throws DbException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ try {
+ String sql = "SELECT NULL FROM outgoingKeys"
+ + " WHERE contactId = ? AND transportId = ?";
+ ps = txn.prepareStatement(sql);
+ ps.setInt(1, c.getInt());
+ ps.setString(2, t.getString());
+ rs = ps.executeQuery();
+ boolean found = rs.next();
+ rs.close();
+ ps.close();
+ return found;
+ } catch (SQLException e) {
+ tryToClose(rs, LOG, WARNING);
+ tryToClose(ps, LOG, WARNING);
+ throw new DbException(e);
+ }
+ }
+
@Override
public boolean containsVisibleMessage(Connection txn, ContactId c,
MessageId m) throws DbException {
@@ -2605,6 +2628,38 @@ abstract class JdbcDatabase implements Database {
}
}
+ @Override
+ public Map> getTransportsWithKeys(
+ Connection txn) throws DbException {
+ Statement s = null;
+ ResultSet rs = null;
+ try {
+ String sql = "SELECT DISTINCT contactId, transportId"
+ + " FROM outgoingKeys";
+ s = txn.createStatement();
+ rs = s.executeQuery(sql);
+ Map> ids = new HashMap<>();
+ while (rs.next()) {
+ ContactId c = new ContactId(rs.getInt(1));
+ TransportId t = new TransportId(rs.getString(2));
+ Collection transportIds = ids.get(c);
+ if (transportIds == null) {
+ transportIds = new ArrayList<>();
+ ids.put(c, transportIds);
+ }
+ transportIds.add(t);
+ }
+ rs.close();
+ s.close();
+ return ids;
+ } catch (SQLException e) {
+ tryToClose(rs, LOG, WARNING);
+ tryToClose(s, LOG, WARNING);
+ tryToClose(s, LOG, WARNING);
+ throw new DbException(e);
+ }
+ }
+
@Override
public void incrementStreamCounter(Connection txn, TransportId t,
KeySetId k) throws DbException {
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
index 69bc97671..2da33c18d 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
@@ -698,7 +698,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction();
// Initially there should be no transport keys in the database
+ assertFalse(db.containsTransportKeys(txn, contactId, transportId));
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
+ assertTrue(db.getTransportsWithKeys(txn).isEmpty());
// Add the contact, the transport and the transport keys
db.addIdentity(txn, identity);
@@ -709,6 +711,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
// Retrieve the transport keys
+ assertTrue(db.containsTransportKeys(txn, contactId, transportId));
Collection allKeys =
db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size());
@@ -721,6 +724,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertKeysEquals(keys1, ks.getKeys());
}
}
+ assertEquals(singletonMap(contactId, singletonList(transportId)),
+ db.getTransportsWithKeys(txn));
// Update the transport keys
TransportKeys updated = createTransportKeys(timePeriod + 1, active);
@@ -732,6 +737,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
null, updated1));
// Retrieve the transport keys again
+ assertTrue(db.containsTransportKeys(txn, contactId, transportId));
allKeys = db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) {
@@ -743,10 +749,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertKeysEquals(updated1, ks.getKeys());
}
}
+ assertEquals(singletonMap(contactId, singletonList(transportId)),
+ db.getTransportsWithKeys(txn));
// Removing the contact should remove the transport keys
db.removeContact(txn, contactId);
+ assertFalse(db.containsTransportKeys(txn, contactId, transportId));
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
+ assertTrue(db.getTransportsWithKeys(txn).isEmpty());
db.commitTransaction(txn);
db.close();