mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Added a flag for making groups visible to future contacts.
This commit is contained in:
@@ -344,11 +344,18 @@ public interface DatabaseComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the given group visible to the given set of contacts and invisible
|
* Makes the given group visible to the given set of contacts and invisible
|
||||||
* to any other contacts.
|
* to any other current or future contacts.
|
||||||
*/
|
*/
|
||||||
void setVisibility(GroupId g, Collection<ContactId> visible)
|
void setVisibility(GroupId g, Collection<ContactId> visible)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the given group visible or invisible to future contacts by default.
|
||||||
|
* If <tt>visible</tt> is true, the group is also made visible to all
|
||||||
|
* current contacts.
|
||||||
|
*/
|
||||||
|
void setVisibleToAll(GroupId g, boolean visible) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes to the given group, or returns false if the user already has
|
* Subscribes to the given group, or returns false if the user already has
|
||||||
* the maximum number of subscriptions.
|
* the maximum number of subscriptions.
|
||||||
|
|||||||
@@ -791,6 +791,13 @@ interface Database<T> {
|
|||||||
void setTransportUpdateAcked(T txn, ContactId c, TransportId t,
|
void setTransportUpdateAcked(T txn, ContactId c, TransportId t,
|
||||||
long version) throws DbException;
|
long version) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the given group visible or invisible to future contacts by default.
|
||||||
|
* <p>
|
||||||
|
* Locking: subscription write.
|
||||||
|
*/
|
||||||
|
void setVisibleToAll(T txn, GroupId g, boolean visible) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the expiry times of the given messages with respect to the given
|
* Updates the expiry times of the given messages with respect to the given
|
||||||
* contact, using the given transmission counts and the latency of the
|
* contact, using the given transmission counts and the latency of the
|
||||||
|
|||||||
@@ -1982,22 +1982,61 @@ DatabaseCleaner.Callback {
|
|||||||
if(!db.containsSubscription(txn, g))
|
if(!db.containsSubscription(txn, g))
|
||||||
throw new NoSuchSubscriptionException();
|
throw new NoSuchSubscriptionException();
|
||||||
// Use HashSets for O(1) lookups, O(n) overall running time
|
// Use HashSets for O(1) lookups, O(n) overall running time
|
||||||
HashSet<ContactId> newVisible =
|
HashSet<ContactId> now = new HashSet<ContactId>(visible);
|
||||||
new HashSet<ContactId>(visible);
|
Collection<ContactId> before = db.getVisibility(txn, g);
|
||||||
HashSet<ContactId> oldVisible =
|
before = new HashSet<ContactId>(before);
|
||||||
new HashSet<ContactId>(db.getVisibility(txn, g));
|
|
||||||
// Set the group's visibility for each current contact
|
// Set the group's visibility for each current contact
|
||||||
for(ContactId c : db.getContactIds(txn)) {
|
for(ContactId c : db.getContactIds(txn)) {
|
||||||
boolean then = oldVisible.contains(c);
|
boolean wasBefore = before.contains(c);
|
||||||
boolean now = newVisible.contains(c);
|
boolean isNow = now.contains(c);
|
||||||
if(!then && now) {
|
if(!wasBefore && isNow) {
|
||||||
db.addVisibility(txn, c, g);
|
db.addVisibility(txn, c, g);
|
||||||
affected.add(c);
|
affected.add(c);
|
||||||
} else if(then && !now) {
|
} else if(wasBefore && !isNow) {
|
||||||
db.removeVisibility(txn, c, g);
|
db.removeVisibility(txn, c, g);
|
||||||
affected.add(c);
|
affected.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Make the group invisible to future contacts
|
||||||
|
db.setVisibleToAll(txn, g, false);
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
subscriptionLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
contactLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
if(!affected.isEmpty())
|
||||||
|
callListeners(new LocalSubscriptionsUpdatedEvent(affected));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisibleToAll(GroupId g, boolean visible) throws DbException {
|
||||||
|
Collection<ContactId> affected = new ArrayList<ContactId>();
|
||||||
|
contactLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
subscriptionLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
T txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
if(!db.containsSubscription(txn, g))
|
||||||
|
throw new NoSuchSubscriptionException();
|
||||||
|
// Make the group visible or invisible to future contacts
|
||||||
|
db.setVisibleToAll(txn, g, visible);
|
||||||
|
if(visible) {
|
||||||
|
// Make the group visible to all current contacts
|
||||||
|
Collection<ContactId> before = db.getVisibility(txn, g);
|
||||||
|
before = new HashSet<ContactId>(before);
|
||||||
|
for(ContactId c : db.getContactIds(txn)) {
|
||||||
|
if(!before.contains(c)) {
|
||||||
|
db.addVisibility(txn, c, g);
|
||||||
|
affected.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " (groupId HASH NOT NULL,"
|
+ " (groupId HASH NOT NULL,"
|
||||||
+ " name VARCHAR NOT NULL,"
|
+ " name VARCHAR NOT NULL,"
|
||||||
+ " publicKey BINARY," // Null for unrestricted groups
|
+ " publicKey BINARY," // Null for unrestricted groups
|
||||||
|
+ " visibleToAll BOOLEAN NOT NULL,"
|
||||||
+ " PRIMARY KEY (groupId))";
|
+ " PRIMARY KEY (groupId))";
|
||||||
|
|
||||||
// Locking: subscription
|
// Locking: subscription
|
||||||
@@ -573,6 +574,27 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if(rs.next()) throw new DbStateException();
|
if(rs.next()) throw new DbStateException();
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
|
// Make groups that are visible to everyone visible to this contact
|
||||||
|
sql = "SELECT groupId FROM groups WHERE visibleToAll = TRUE";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
Collection<byte[]> ids = new ArrayList<byte[]>();
|
||||||
|
while(rs.next()) ids.add(rs.getBytes(1));
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
if(!ids.isEmpty()) {
|
||||||
|
sql = "INSERT INTO groupVisibilities (contactId, groupId)"
|
||||||
|
+ " VALUES (?, ?)";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
for(byte[] id : ids) {
|
||||||
|
ps.setBytes(2, id);
|
||||||
|
ps.addBatch();
|
||||||
|
}
|
||||||
|
affected = ps.executeUpdate();
|
||||||
|
if(affected != ids.size()) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
// Create a connection time row
|
// Create a connection time row
|
||||||
sql = "INSERT INTO connectionTimes (contactId, lastConnected)"
|
sql = "INSERT INTO connectionTimes (contactId, lastConnected)"
|
||||||
+ " VALUES (?, ?)";
|
+ " VALUES (?, ?)";
|
||||||
@@ -893,8 +915,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
if(count > MAX_SUBSCRIPTIONS) throw new DbStateException();
|
if(count > MAX_SUBSCRIPTIONS) throw new DbStateException();
|
||||||
if(count == MAX_SUBSCRIPTIONS) return false;
|
if(count == MAX_SUBSCRIPTIONS) return false;
|
||||||
sql = "INSERT INTO groups (groupId, name, publicKey)"
|
sql = "INSERT INTO groups (groupId, name, publicKey, visibleToAll)"
|
||||||
+ " VALUES (?, ?, ?)";
|
+ " VALUES (?, ?, ?, FALSE)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, g.getId().getBytes());
|
ps.setBytes(1, g.getId().getBytes());
|
||||||
ps.setString(2, g.getName());
|
ps.setString(2, g.getName());
|
||||||
@@ -3376,6 +3398,23 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setVisibleToAll(Connection txn, GroupId g, boolean visible)
|
||||||
|
throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
try {
|
||||||
|
String sql = "UPDATE groups SET visibleToAll = ? WHERE groupId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBoolean(1, visible);
|
||||||
|
ps.setBytes(2, g.getBytes());
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if(affected > 1) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateExpiryTimes(Connection txn, ContactId c,
|
public void updateExpiryTimes(Connection txn, ContactId c,
|
||||||
Map<MessageId, Integer> sent, long maxLatency) throws DbException {
|
Map<MessageId, Integer> sent, long maxLatency) throws DbException {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
|
|||||||
@@ -1729,6 +1729,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
|||||||
oneOf(database).getContactIds(txn);
|
oneOf(database).getContactIds(txn);
|
||||||
will(returnValue(both));
|
will(returnValue(both));
|
||||||
oneOf(database).removeVisibility(txn, contactId1, groupId);
|
oneOf(database).removeVisibility(txn, contactId1, groupId);
|
||||||
|
oneOf(database).setVisibleToAll(txn, groupId, false);
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
oneOf(listener).eventOccurred(with(any(
|
oneOf(listener).eventOccurred(with(any(
|
||||||
LocalSubscriptionsUpdatedEvent.class)));
|
LocalSubscriptionsUpdatedEvent.class)));
|
||||||
@@ -1745,7 +1746,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testNotChangingVisibilityDoesNotCallListeners()
|
public void testNotChangingVisibilityDoesNotCallListeners()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
final ContactId contactId1 = new ContactId(234);
|
final ContactId contactId1 = new ContactId(123);
|
||||||
final Collection<ContactId> both = Arrays.asList(contactId, contactId1);
|
final Collection<ContactId> both = Arrays.asList(contactId, contactId1);
|
||||||
Mockery context = new Mockery();
|
Mockery context = new Mockery();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -1762,6 +1763,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
|||||||
will(returnValue(both));
|
will(returnValue(both));
|
||||||
oneOf(database).getContactIds(txn);
|
oneOf(database).getContactIds(txn);
|
||||||
will(returnValue(both));
|
will(returnValue(both));
|
||||||
|
oneOf(database).setVisibleToAll(txn, groupId, false);
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||||
@@ -1773,6 +1775,57 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSettingVisibleToAllTrueAffectsCurrentContacts()
|
||||||
|
throws Exception {
|
||||||
|
final ContactId contactId1 = new ContactId(123);
|
||||||
|
final Collection<ContactId> both = Arrays.asList(contactId, contactId1);
|
||||||
|
Mockery context = new Mockery();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Database<Object> database = context.mock(Database.class);
|
||||||
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
|
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||||
|
final DatabaseListener listener = context.mock(DatabaseListener.class);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// setVisibility()
|
||||||
|
oneOf(database).startTransaction();
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(database).containsSubscription(txn, groupId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).getVisibility(txn, groupId);
|
||||||
|
will(returnValue(Collections.emptyList()));
|
||||||
|
oneOf(database).getContactIds(txn);
|
||||||
|
will(returnValue(both));
|
||||||
|
oneOf(database).addVisibility(txn, contactId, groupId);
|
||||||
|
oneOf(database).setVisibleToAll(txn, groupId, false);
|
||||||
|
oneOf(database).commitTransaction(txn);
|
||||||
|
oneOf(listener).eventOccurred(with(any(
|
||||||
|
LocalSubscriptionsUpdatedEvent.class)));
|
||||||
|
// setVisibleToAll()
|
||||||
|
oneOf(database).startTransaction();
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(database).containsSubscription(txn, groupId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).setVisibleToAll(txn, groupId, true);
|
||||||
|
oneOf(database).getVisibility(txn, groupId);
|
||||||
|
will(returnValue(Arrays.asList(contactId)));
|
||||||
|
oneOf(database).getContactIds(txn);
|
||||||
|
will(returnValue(both));
|
||||||
|
oneOf(database).addVisibility(txn, contactId1, groupId);
|
||||||
|
oneOf(database).commitTransaction(txn);
|
||||||
|
oneOf(listener).eventOccurred(with(any(
|
||||||
|
LocalSubscriptionsUpdatedEvent.class)));
|
||||||
|
}});
|
||||||
|
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||||
|
shutdown);
|
||||||
|
|
||||||
|
db.addListener(listener);
|
||||||
|
db.setVisibility(groupId, Arrays.asList(contactId));
|
||||||
|
db.setVisibleToAll(groupId, true);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTemporarySecrets() throws Exception {
|
public void testTemporarySecrets() throws Exception {
|
||||||
Mockery context = new Mockery();
|
Mockery context = new Mockery();
|
||||||
|
|||||||
@@ -873,6 +873,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
|||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||||
db.addSubscription(txn, group);
|
db.addSubscription(txn, group);
|
||||||
|
db.addVisibility(txn, contactId, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
||||||
|
|
||||||
// The message is not in the database
|
// The message is not in the database
|
||||||
@@ -891,6 +892,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
|||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||||
db.addSubscription(txn, group);
|
db.addSubscription(txn, group);
|
||||||
|
db.addVisibility(txn, contactId, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
||||||
db.addGroupMessage(txn, message);
|
db.addGroupMessage(txn, message);
|
||||||
|
|
||||||
@@ -915,6 +917,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
|||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||||
db.addSubscription(txn, group);
|
db.addSubscription(txn, group);
|
||||||
|
db.addVisibility(txn, contactId, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
||||||
db.addGroupMessage(txn, message);
|
db.addGroupMessage(txn, message);
|
||||||
|
|
||||||
@@ -1028,6 +1031,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
|||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||||
db.addSubscription(txn, group);
|
db.addSubscription(txn, group);
|
||||||
|
db.addVisibility(txn, contactId, groupId);
|
||||||
db.addGroupMessage(txn, message);
|
db.addGroupMessage(txn, message);
|
||||||
db.addStatus(txn, contactId, messageId, false);
|
db.addStatus(txn, contactId, messageId, false);
|
||||||
|
|
||||||
@@ -1048,8 +1052,8 @@ public class H2DatabaseTest extends BriarTestCase {
|
|||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||||
db.addSubscription(txn, group);
|
db.addSubscription(txn, group);
|
||||||
db.addGroupMessage(txn, message);
|
|
||||||
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
|
||||||
|
db.addGroupMessage(txn, message);
|
||||||
db.addStatus(txn, contactId, messageId, false);
|
db.addStatus(txn, contactId, messageId, false);
|
||||||
|
|
||||||
// The subscription is not visible
|
// The subscription is not visible
|
||||||
|
|||||||
Reference in New Issue
Block a user