Merge branch '756-group-visibility' into 'master'

Add third group visibility state

This branch adds a third group visibility state: each group is either invisible, visible, or shared with respect to each contact.

Invisible means that the contact doesn't see any sign that we subscribe to the group. Visible means that the contact can send us messages in the group, but we won't send the contact messages in the group. Shared means that the contact can send us messages in the group and we'll send the contact any shared messages in the group.

This is a preparatory step for #756. I'll put up another MR with the changes that close that ticket.

See merge request !410
This commit is contained in:
akwizgran
2016-11-16 15:56:37 +00:00
19 changed files with 585 additions and 430 deletions

View File

@@ -66,6 +66,7 @@ import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US; import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US;
import static org.briarproject.api.privategroup.Visibility.VISIBLE; import static org.briarproject.api.privategroup.Visibility.VISIBLE;
import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID; import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID; import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING; import static org.briarproject.api.sync.ValidationManager.State.PENDING;
@@ -348,9 +349,9 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
assertEquals(joinMsg0.getMessage().getId(), assertEquals(joinMsg0.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0)); groupManager0.getPreviousMsgId(groupId0));
// make group visible to 1 // share the group with 1
Transaction txn0 = db0.startTransaction(false); Transaction txn0 = db0.startTransaction(false);
db0.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), true); db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0); db0.commitTransaction(txn0);
db0.endTransaction(txn0); db0.endTransaction(txn0);
@@ -371,9 +372,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
assertEquals(joinMsg1.getMessage().getId(), assertEquals(joinMsg1.getMessage().getId(),
groupManager1.getPreviousMsgId(groupId0)); groupManager1.getPreviousMsgId(groupId0));
// make group visible to 0 // share the group with 0
Transaction txn1 = db1.startTransaction(false); Transaction txn1 = db1.startTransaction(false);
db1.setVisibleToContact(txn1, contactId01, privateGroup0.getId(), true); db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(),
SHARED);
db1.commitTransaction(txn1); db1.commitTransaction(txn1);
db1.endTransaction(txn1); db1.endTransaction(txn1);
@@ -415,9 +417,9 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
assertEquals(joinMsg0.getMessage().getId(), assertEquals(joinMsg0.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0)); groupManager0.getPreviousMsgId(groupId0));
// make group visible to 1 // share the group with 1
Transaction txn0 = db0.startTransaction(false); Transaction txn0 = db0.startTransaction(false);
db0.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), true); db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0); db0.commitTransaction(txn0);
db0.endTransaction(txn0); db0.endTransaction(txn0);
@@ -438,9 +440,9 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
assertEquals(joinMsg1.getMessage().getId(), assertEquals(joinMsg1.getMessage().getId(),
groupManager1.getPreviousMsgId(groupId0)); groupManager1.getPreviousMsgId(groupId0));
// make group visible to 0 // share the group with 0
Transaction txn1 = db1.startTransaction(false); Transaction txn1 = db1.startTransaction(false);
db1.setVisibleToContact(txn1, contactId01, privateGroup0.getId(), true); db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(), SHARED);
db1.commitTransaction(txn1); db1.commitTransaction(txn1);
db1.endTransaction(txn1); db1.endTransaction(txn1);
@@ -518,9 +520,9 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
public void testRevealingRelationships() throws Exception { public void testRevealingRelationships() throws Exception {
defaultInit(); defaultInit();
// make group visible to 2 // share the group with 2
Transaction txn0 = db0.startTransaction(false); Transaction txn0 = db0.startTransaction(false);
db0.setVisibleToContact(txn0, contactId2, privateGroup0.getId(), true); db0.setGroupVisibility(txn0, contactId2, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0); db0.commitTransaction(txn0);
db0.endTransaction(txn0); db0.endTransaction(txn0);
@@ -540,8 +542,9 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
Transaction txn2 = db2.startTransaction(false); Transaction txn2 = db2.startTransaction(false);
groupManager2.addPrivateGroup(txn2, privateGroup0, joinMsg2, false); groupManager2.addPrivateGroup(txn2, privateGroup0, joinMsg2, false);
// make group visible to 0 // share the group with 0
db2.setVisibleToContact(txn2, contactId01, privateGroup0.getId(), true); db2.setGroupVisibility(txn2, contactId01, privateGroup0.getId(),
SHARED);
db2.commitTransaction(txn2); db2.commitTransaction(txn2);
db2.endTransaction(txn2); db2.endTransaction(txn2);
@@ -741,9 +744,9 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
assertEquals(joinMsg0.getMessage().getId(), assertEquals(joinMsg0.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0)); groupManager0.getPreviousMsgId(groupId0));
// make group visible to 1 // share the group with 1
Transaction txn0 = db0.startTransaction(false); Transaction txn0 = db0.startTransaction(false);
db0.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), true); db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0); db0.commitTransaction(txn0);
db0.endTransaction(txn0); db0.endTransaction(txn0);
@@ -762,9 +765,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
inviteTime, creatorSignature); inviteTime, creatorSignature);
groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false); groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false);
// make group visible to 0 // share the group with 0
Transaction txn1 = db1.startTransaction(false); Transaction txn1 = db1.startTransaction(false);
db1.setVisibleToContact(txn1, contactId01, privateGroup0.getId(), true); db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(),
SHARED);
db1.commitTransaction(txn1); db1.commitTransaction(txn1);
db1.endTransaction(txn1); db1.endTransaction(txn1);
assertEquals(joinMsg1.getMessage().getId(), assertEquals(joinMsg1.getMessage().getId(),

View File

@@ -11,6 +11,7 @@ import org.briarproject.api.settings.Settings;
import org.briarproject.api.sync.Ack; import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Group.Visibility;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -232,6 +233,15 @@ public interface DatabaseComponent {
*/ */
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException; Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
/**
* Returns the given group's visibility to the given contact, or
* {@link Visibility INVISIBLE} if the group is not in the database.
* <p/>
* Read-only.
*/
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -388,14 +398,6 @@ public interface DatabaseComponent {
void incrementStreamCounter(Transaction txn, ContactId c, TransportId t, void incrementStreamCounter(Transaction txn, ContactId c, TransportId t,
long rotationPeriod) throws DbException; long rotationPeriod) throws DbException;
/**
* Returns true if the given group is visible to the given contact.
* <p/>
* Read-only.
*/
boolean isVisibleToContact(Transaction txn, ContactId c, GroupId g)
throws DbException;
/** /**
* Merges the given metadata with the existing metadata for the given * Merges the given metadata with the existing metadata for the given
* group. * group.
@@ -470,6 +472,12 @@ public interface DatabaseComponent {
void setContactActive(Transaction txn, ContactId c, boolean active) void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException; throws DbException;
/**
* Sets the given group's visibility to the given contact.
*/
void setGroupVisibility(Transaction txn, ContactId c, GroupId g,
Visibility v) throws DbException;
/** /**
* Marks the given message as shared. * Marks the given message as shared.
*/ */
@@ -494,12 +502,6 @@ public interface DatabaseComponent {
void setReorderingWindow(Transaction txn, ContactId c, TransportId t, void setReorderingWindow(Transaction txn, ContactId c, TransportId t,
long rotationPeriod, long base, byte[] bitmap) throws DbException; long rotationPeriod, long base, byte[] bitmap) throws DbException;
/**
* Makes a group visible or invisible to a contact.
*/
void setVisibleToContact(Transaction txn, ContactId c, GroupId g,
boolean visible) throws DbException;
/** /**
* Stores the given transport keys, deleting any keys they have replaced. * Stores the given transport keys, deleting any keys they have replaced.
*/ */

View File

@@ -2,6 +2,12 @@ package org.briarproject.api.sync;
public class Group { public class Group {
public enum Visibility {
INVISIBLE, // The group is not visible
VISIBLE, // The group is visible but messages are not shared
SHARED // The group is visible and messages are shared
}
private final GroupId id; private final GroupId id;
private final ClientId clientId; private final ClientId clientId;
private final byte[] descriptor; private final byte[] descriptor;

View File

@@ -12,7 +12,6 @@ import org.briarproject.api.blogs.MessageType;
import org.briarproject.api.clients.Client; import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry; import org.briarproject.api.data.BdfEntry;
@@ -68,6 +67,7 @@ import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
import static org.briarproject.api.blogs.MessageType.WRAPPED_POST; import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
import static org.briarproject.api.contact.ContactManager.AddContactHook; import static org.briarproject.api.contact.ContactManager.AddContactHook;
import static org.briarproject.api.contact.ContactManager.RemoveContactHook; import static org.briarproject.api.contact.ContactManager.RemoveContactHook;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.blogs.BlogPostValidator.authorToBdfDictionary; import static org.briarproject.blogs.BlogPostValidator.authorToBdfDictionary;
@NotNullByDefault @NotNullByDefault
@@ -96,35 +96,24 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
@Override @Override
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
// Ensure that the local identity has its own personal blog // Create our personal blog if necessary
LocalAuthor la = identityManager.getLocalAuthor(txn); LocalAuthor a = identityManager.getLocalAuthor(txn);
Blog b = blogFactory.createBlog(la); Blog b = blogFactory.createBlog(a);
Group g = b.getGroup(); db.addGroup(txn, b.getGroup());
if (!db.containsGroup(txn, g.getId())) { // Ensure that we have the personal blogs of all contacts
db.addGroup(txn, g);
for (ContactId c : db.getContacts(txn, la.getId())) {
db.setVisibleToContact(txn, c, g.getId(), true);
}
}
// Ensure that we have the personal blogs of all pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c); for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// get personal blog of the contact // Add the personal blog of the contact and share it with the contact
Blog b = blogFactory.createBlog(c.getAuthor()); Blog b = blogFactory.createBlog(c.getAuthor());
Group g = b.getGroup(); db.addGroup(txn, b.getGroup());
if (!db.containsGroup(txn, g.getId())) { db.setGroupVisibility(txn, c.getId(), b.getId(), SHARED);
// add the personal blog of the contact // Share our personal blog with the contact
db.addGroup(txn, g); LocalAuthor a = identityManager.getLocalAuthor(txn);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); Blog b2 = blogFactory.createBlog(a);
db.setGroupVisibility(txn, c.getId(), b2.getId(), SHARED);
// share our personal blog with the new contact
LocalAuthor a = db.getLocalAuthor(txn, c.getLocalAuthorId());
Blog b2 = blogFactory.createBlog(a);
db.setVisibleToContact(txn, c.getId(), b2.getId(), true);
}
} }
@Override @Override

View File

@@ -12,6 +12,7 @@ import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.settings.Settings; import org.briarproject.api.settings.Settings;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Group.Visibility;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -72,6 +73,13 @@ interface Database<T> {
*/ */
void addGroup(T txn, Group g) throws DbException; void addGroup(T txn, Group g) throws DbException;
/**
* Sets the given group's visibility to the given contact to either
* {@link Visibility VISIBLE} or {@link Visibility SHARED}.
*/
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
throws DbException;
/** /**
* Stores a local pseudonym. * Stores a local pseudonym.
*/ */
@@ -98,7 +106,7 @@ interface Database<T> {
* Initialises the status of the given message with respect to the given * Initialises the status of the given message with respect to the given
* contact. * contact.
* *
* @param ack whether the message needs to be acknowledged. * @param ack whether the message needs to be acknowledged.
* @param seen whether the contact has seen the message. * @param seen whether the contact has seen the message.
*/ */
void addStatus(T txn, ContactId c, MessageId m, boolean ack, boolean seen) void addStatus(T txn, ContactId c, MessageId m, boolean ack, boolean seen)
@@ -116,11 +124,6 @@ interface Database<T> {
void addTransportKeys(T txn, ContactId c, TransportKeys k) void addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException; throws DbException;
/**
* Makes a group visible to the given contact.
*/
void addVisibility(T txn, ContactId c, GroupId g) throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact for the given
* local pseudonym. * local pseudonym.
@@ -166,17 +169,9 @@ interface Database<T> {
boolean containsTransport(T txn, TransportId t) throws DbException; boolean containsTransport(T txn, TransportId t) throws DbException;
/** /**
* Returns true if the database contains the given group and the group is * Returns true if the database contains the given message, the message is
* visible to the given contact. * shared, and the visibility of the message's group to the given contact
* <p/> * is either {@link Visibility VISIBLE} or {@link Visibility SHARED}.
* Read-only.
*/
boolean containsVisibleGroup(T txn, ContactId c, GroupId g)
throws DbException;
/**
* Returns true if the database contains the given message and the message
* is visible to the given contact.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
@@ -267,6 +262,24 @@ interface Database<T> {
*/ */
Collection<Group> getGroups(T txn, ClientId c) throws DbException; Collection<Group> getGroups(T txn, ClientId c) throws DbException;
/**
* Returns the given group's visibility to the given contact, or
* {@link Visibility INVISIBLE} if the group is not in the database.
* <p/>
* Read-only.
*/
Visibility getGroupVisibility(T txn, ContactId c, GroupId g)
throws DbException;
/**
* Returns the IDs of all contacts to which the given group's visibility is
* either {@link Visibility VISIBLE} or {@link Visibility SHARED}.
* <p/>
* Read-only.
*/
Collection<ContactId> getGroupVisibility(T txn, GroupId g)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -477,13 +490,6 @@ interface Database<T> {
Map<ContactId, TransportKeys> getTransportKeys(T txn, TransportId t) Map<ContactId, TransportKeys> getTransportKeys(T txn, TransportId t)
throws DbException; throws DbException;
/**
* Returns the IDs of all contacts to which the given group is visible.
* <p/>
* Read-only.
*/
Collection<ContactId> getVisibility(T txn, GroupId g) throws DbException;
/** /**
* Increments the outgoing stream counter for the given contact and * Increments the outgoing stream counter for the given contact and
* transport in the given rotation period. * transport in the given rotation period.
@@ -550,6 +556,13 @@ interface Database<T> {
*/ */
void removeGroup(T txn, GroupId g) throws DbException; void removeGroup(T txn, GroupId g) throws DbException;
/**
* Sets the given group's visibility to the given contact to
* {@link Visibility INVISIBLE}.
*/
void removeGroupVisibility(T txn, ContactId c, GroupId g)
throws DbException;
/** /**
* Removes a local pseudonym (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
@@ -585,11 +598,6 @@ interface Database<T> {
*/ */
void removeTransport(T txn, TransportId t) throws DbException; void removeTransport(T txn, TransportId t) throws DbException;
/**
* Makes a group invisible to the given contact.
*/
void removeVisibility(T txn, ContactId c, GroupId g) throws DbException;
/** /**
* Resets the transmission count and expiry time of the given message with * Resets the transmission count and expiry time of the given message with
* respect to the given contact. * respect to the given contact.
@@ -607,6 +615,13 @@ interface Database<T> {
void setContactActive(T txn, ContactId c, boolean active) void setContactActive(T txn, ContactId c, boolean active)
throws DbException; throws DbException;
/**
* Sets the given group's visibility to the given contact to either
* {@link Visibility VISIBLE} or {@link Visibility SHARED}.
*/
void setGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
throws DbException;
/** /**
* Marks the given message as shared. * Marks the given message as shared.
*/ */

View File

@@ -42,6 +42,7 @@ import org.briarproject.api.settings.Settings;
import org.briarproject.api.sync.Ack; import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Group.Visibility;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -66,6 +67,8 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN; import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES; import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
@@ -97,6 +100,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public boolean open() throws DbException { public boolean open() throws DbException {
Runnable shutdownHook = new Runnable() { Runnable shutdownHook = new Runnable() {
@Override
public void run() { public void run() {
try { try {
close(); close();
@@ -223,7 +227,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
private void addMessage(T txn, Message m, State state, boolean shared, private void addMessage(T txn, Message m, State state, boolean shared,
@Nullable ContactId sender) throws DbException { @Nullable ContactId sender) throws DbException {
db.addMessage(txn, m, state, shared); db.addMessage(txn, m, state, shared);
for (ContactId c : db.getVisibility(txn, m.getGroupId())) { for (ContactId c : db.getGroupVisibility(txn, m.getGroupId())) {
boolean offered = db.removeOfferedMessage(txn, c, m.getId()); boolean offered = db.removeOfferedMessage(txn, c, m.getId());
boolean seen = offered || c.equals(sender); boolean seen = offered || c.equals(sender);
db.addStatus(txn, c, m.getId(), seen, seen); db.addStatus(txn, c, m.getId(), seen, seen);
@@ -441,6 +445,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getGroups(txn, c); return db.getGroups(txn, c);
} }
@Override
public Visibility getGroupVisibility(Transaction transaction, ContactId c,
GroupId g) throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
return db.getGroupVisibility(txn, c, g);
}
@Override @Override
public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a) public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -602,17 +615,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.incrementStreamCounter(txn, c, t, rotationPeriod); db.incrementStreamCounter(txn, c, t, rotationPeriod);
} }
@Override
public boolean isVisibleToContact(Transaction transaction, ContactId c,
GroupId g) throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
return db.containsVisibleGroup(txn, c, g);
}
@Override @Override
public void mergeGroupMetadata(Transaction transaction, GroupId g, public void mergeGroupMetadata(Transaction transaction, GroupId g,
Metadata meta) throws DbException { Metadata meta) throws DbException {
@@ -672,7 +674,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
if (db.containsVisibleGroup(txn, c, m.getGroupId())) { if (db.getGroupVisibility(txn, c, m.getGroupId()) != INVISIBLE) {
if (db.containsMessage(txn, m.getId())) { if (db.containsMessage(txn, m.getId())) {
db.raiseSeenFlag(txn, c, m.getId()); db.raiseSeenFlag(txn, c, m.getId());
db.raiseAckFlag(txn, c, m.getId()); db.raiseAckFlag(txn, c, m.getId());
@@ -745,7 +747,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
GroupId id = g.getId(); GroupId id = g.getId();
if (!db.containsGroup(txn, id)) if (!db.containsGroup(txn, id))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
Collection<ContactId> affected = db.getVisibility(txn, id); Collection<ContactId> affected = db.getGroupVisibility(txn, id);
db.removeGroup(txn, id); db.removeGroup(txn, id);
transaction.attach(new GroupRemovedEvent(g)); transaction.attach(new GroupRemovedEvent(g));
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));
@@ -794,6 +796,34 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new ContactStatusChangedEvent(c, active)); transaction.attach(new ContactStatusChangedEvent(c, active));
} }
@Override
public void setGroupVisibility(Transaction transaction, ContactId c,
GroupId g, Visibility v) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
Visibility old = db.getGroupVisibility(txn, c, g);
if (old == v) return;
if (old == INVISIBLE) {
db.addGroupVisibility(txn, c, g, v == SHARED);
for (MessageId m : db.getMessageIds(txn, g)) {
boolean seen = db.removeOfferedMessage(txn, c, m);
db.addStatus(txn, c, m, seen, seen);
}
} else if (v == INVISIBLE) {
db.removeGroupVisibility(txn, c, g);
for (MessageId m : db.getMessageIds(txn, g))
db.removeStatus(txn, c, m);
} else {
db.setGroupVisibility(txn, c, g, v == SHARED);
}
List<ContactId> affected = Collections.singletonList(c);
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
}
@Override @Override
public void setMessageShared(Transaction transaction, MessageId m) public void setMessageShared(Transaction transaction, MessageId m)
throws DbException { throws DbException {
@@ -845,33 +875,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap); db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap);
} }
@Override
public void setVisibleToContact(Transaction transaction, ContactId c,
GroupId g, boolean visible) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
boolean wasVisible = db.containsVisibleGroup(txn, c, g);
if (visible && !wasVisible) {
db.addVisibility(txn, c, g);
for (MessageId m : db.getMessageIds(txn, g)) {
boolean seen = db.removeOfferedMessage(txn, c, m);
db.addStatus(txn, c, m, seen, seen);
}
} else if (!visible && wasVisible) {
db.removeVisibility(txn, c, g);
for (MessageId m : db.getMessageIds(txn, g))
db.removeStatus(txn, c, m);
}
if (visible != wasVisible) {
List<ContactId> affected = Collections.singletonList(c);
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
}
}
@Override @Override
public void updateTransportKeys(Transaction transaction, public void updateTransportKeys(Transaction transaction,
Map<ContactId, TransportKeys> keys) throws DbException { Map<ContactId, TransportKeys> keys) throws DbException {

File diff suppressed because it is too large Load Diff

View File

@@ -73,6 +73,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ; import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
@NotNullByDefault @NotNullByDefault
@@ -116,7 +117,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
if (db.containsGroup(txn, g.getId())) return; if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
BdfDictionary gm = new BdfDictionary(); BdfDictionary gm = new BdfDictionary();
gm.put(CONTACT, c.getId().getInt()); gm.put(CONTACT, c.getId().getInt());

View File

@@ -33,6 +33,7 @@ import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ; import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
@NotNullByDefault @NotNullByDefault
@@ -64,7 +65,7 @@ class MessagingManagerImpl extends ConversationClientImpl
if (db.containsGroup(txn, g.getId())) return; if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
BdfDictionary d = new BdfDictionary(); BdfDictionary d = new BdfDictionary();
d.put("contactId", c.getId().getInt()); d.put("contactId", c.getId().getInt());

View File

@@ -18,6 +18,7 @@ import org.briarproject.api.privategroup.PrivateGroup;
import org.briarproject.api.privategroup.PrivateGroupFactory; import org.briarproject.api.privategroup.PrivateGroupFactory;
import org.briarproject.api.privategroup.PrivateGroupManager; import org.briarproject.api.privategroup.PrivateGroupManager;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Group.Visibility;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -90,11 +91,10 @@ abstract class AbstractProtocolEngine<S extends Session>
return dependency.equals(expected); return dependency.equals(expected);
} }
void syncPrivateGroupWithContact(Transaction txn, S session, boolean sync) void setPrivateGroupVisibility(Transaction txn, S session, Visibility v)
throws DbException, FormatException { throws DbException, FormatException {
ContactId contactId = getContactId(txn, session.getContactGroupId()); ContactId contactId = getContactId(txn, session.getContactGroupId());
db.setVisibleToContact(txn, contactId, session.getPrivateGroupId(), db.setGroupVisibility(txn, contactId, session.getPrivateGroupId(), v);
sync);
} }
Message sendInviteMessage(Transaction txn, S session, Message sendInviteMessage(Transaction txn, S session,

View File

@@ -22,6 +22,8 @@ import org.briarproject.api.system.Clock;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.privategroup.invitation.CreatorState.DISSOLVED; import static org.briarproject.privategroup.invitation.CreatorState.DISSOLVED;
import static org.briarproject.privategroup.invitation.CreatorState.ERROR; import static org.briarproject.privategroup.invitation.CreatorState.ERROR;
import static org.briarproject.privategroup.invitation.CreatorState.INVITED; import static org.briarproject.privategroup.invitation.CreatorState.INVITED;
@@ -158,8 +160,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
private CreatorSession onLocalLeave(Transaction txn, CreatorSession s) private CreatorSession onLocalLeave(Transaction txn, CreatorSession s)
throws DbException { throws DbException {
try { try {
// Stop syncing the private group with the contact // Make the private group invisible to the contact
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group metadata throw new DbException(e); // Invalid group metadata
} }
@@ -183,8 +185,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
// Track the message // Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(), messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false); m.getTimestamp(), false);
// Start syncing the private group with the contact // Share the private group with the contact
syncPrivateGroupWithContact(txn, s, true); setPrivateGroupVisibility(txn, s, SHARED);
// Broadcast an event // Broadcast an event
ContactId contactId = getContactId(txn, m.getContactGroupId()); ContactId contactId = getContactId(txn, m.getContactGroupId());
txn.attach(new GroupInvitationResponseReceivedEvent(contactId, txn.attach(new GroupInvitationResponseReceivedEvent(contactId,
@@ -224,8 +226,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId())) if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s); return abort(txn, s);
// Stop syncing the private group with the contact // Make the private group invisible to the contact
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
// Move to the INVITEE_LEFT state // Move to the INVITEE_LEFT state
return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(), return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(), s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
@@ -236,9 +238,9 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
throws DbException, FormatException { throws DbException, FormatException {
// If the session has already been aborted, do nothing // If the session has already been aborted, do nothing
if (s.getState() == ERROR) return s; if (s.getState() == ERROR) return s;
// If we subscribe, stop syncing the private group with the contact // If we subscribe, make the private group invisible to the contact
if (isSubscribedPrivateGroup(txn, s.getPrivateGroupId())) if (isSubscribedPrivateGroup(txn, s.getPrivateGroupId()))
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
// Send an ABORT message // Send an ABORT message
Message sent = sendAbortMessage(txn, s); Message sent = sendAbortMessage(txn, s);
// Move to the ERROR state // Move to the ERROR state

View File

@@ -45,6 +45,7 @@ import java.util.Map.Entry;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.privategroup.invitation.CreatorState.START; import static org.briarproject.privategroup.invitation.CreatorState.START;
import static org.briarproject.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID; import static org.briarproject.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.privategroup.invitation.MessageType.ABORT; import static org.briarproject.privategroup.invitation.MessageType.ABORT;
@@ -110,7 +111,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
if (db.containsGroup(txn, g.getId())) return; if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt()); meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());

View File

@@ -25,6 +25,8 @@ import org.briarproject.api.system.Clock;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.privategroup.invitation.InviteeState.DISSOLVED; import static org.briarproject.privategroup.invitation.InviteeState.DISSOLVED;
import static org.briarproject.privategroup.invitation.InviteeState.ERROR; import static org.briarproject.privategroup.invitation.InviteeState.ERROR;
import static org.briarproject.privategroup.invitation.InviteeState.INVITED; import static org.briarproject.privategroup.invitation.InviteeState.INVITED;
@@ -157,8 +159,8 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
try { try {
// Subscribe to the private group // Subscribe to the private group
subscribeToPrivateGroup(txn, inviteId); subscribeToPrivateGroup(txn, inviteId);
// Start syncing the private group with the contact // Share the private group with the contact
syncPrivateGroupWithContact(txn, s, true); setPrivateGroupVisibility(txn, s, SHARED);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group metadata throw new DbException(e); // Invalid group metadata
} }
@@ -228,8 +230,8 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
if (!isValidDependency(s, m.getPreviousMessageId())) if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s); return abort(txn, s);
try { try {
// Stop syncing the private group with the contact // Make the private group invisible to the contact
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group metadata throw new DbException(e); // Invalid group metadata
} }
@@ -247,9 +249,9 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
if (s.getState() == ERROR) return s; if (s.getState() == ERROR) return s;
// Mark any invite messages in the session unavailable to answer // Mark any invite messages in the session unavailable to answer
markInvitesUnavailableToAnswer(txn, s); markInvitesUnavailableToAnswer(txn, s);
// Stop syncing the private group with the contact, if we subscribe // If we subscribe, make the private group invisible to the contact
if (isSubscribedPrivateGroup(txn, s.getPrivateGroupId())) if (isSubscribedPrivateGroup(txn, s.getPrivateGroupId()))
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
// Send an ABORT message // Send an ABORT message
Message sent = sendAbortMessage(txn, s); Message sent = sendAbortMessage(txn, s);
// Move to the ERROR state // Move to the ERROR state

View File

@@ -20,6 +20,8 @@ import org.briarproject.api.system.Clock;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.privategroup.invitation.PeerState.AWAIT_MEMBER; import static org.briarproject.privategroup.invitation.PeerState.AWAIT_MEMBER;
import static org.briarproject.privategroup.invitation.PeerState.BOTH_JOINED; import static org.briarproject.privategroup.invitation.PeerState.BOTH_JOINED;
import static org.briarproject.privategroup.invitation.PeerState.ERROR; import static org.briarproject.privategroup.invitation.PeerState.ERROR;
@@ -178,8 +180,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
// Send a JOIN message // Send a JOIN message
Message sent = sendJoinMessage(txn, s, false); Message sent = sendJoinMessage(txn, s, false);
try { try {
// Start syncing the private group with the contact // Share the private group with the contact
syncPrivateGroupWithContact(txn, s, true); setPrivateGroupVisibility(txn, s, SHARED);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group metadata throw new DbException(e); // Invalid group metadata
} }
@@ -195,8 +197,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
// Send a LEAVE message // Send a LEAVE message
Message sent = sendLeaveMessage(txn, s, false); Message sent = sendLeaveMessage(txn, s, false);
try { try {
// Stop syncing the private group with the contact // Make the private group invisible to the contact
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group metadata throw new DbException(e); // Invalid group metadata
} }
@@ -228,8 +230,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
// Send a JOIN message // Send a JOIN message
Message sent = sendJoinMessage(txn, s, false); Message sent = sendJoinMessage(txn, s, false);
try { try {
// Start syncing the private group with the contact // Share the private group with the contact
syncPrivateGroupWithContact(txn, s, true); setPrivateGroupVisibility(txn, s, SHARED);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group metadata throw new DbException(e); // Invalid group metadata
} }
@@ -263,8 +265,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
return abort(txn, s); return abort(txn, s);
// Send a JOIN message // Send a JOIN message
Message sent = sendJoinMessage(txn, s, false); Message sent = sendJoinMessage(txn, s, false);
// Start syncing the private group with the contact // Share the private group with the contact
syncPrivateGroupWithContact(txn, s, true); setPrivateGroupVisibility(txn, s, SHARED);
// Mark the relationship visible to the group, revealed by contact // Mark the relationship visible to the group, revealed by contact
relationshipRevealed(txn, s, true); relationshipRevealed(txn, s, true);
// Move to the BOTH_JOINED state // Move to the BOTH_JOINED state
@@ -277,8 +279,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId())) if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s); return abort(txn, s);
// Start syncing the private group with the contact // Share the private group with the contact
syncPrivateGroupWithContact(txn, s, true); setPrivateGroupVisibility(txn, s, SHARED);
// Mark the relationship visible to the group, revealed by us // Mark the relationship visible to the group, revealed by us
relationshipRevealed(txn, s, false); relationshipRevealed(txn, s, false);
// Move to the BOTH_JOINED state // Move to the BOTH_JOINED state
@@ -314,8 +316,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId())) if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s); return abort(txn, s);
// Stop syncing the private group with the contact // Make the private group invisible to the contact
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
// Move to the LOCAL_JOINED state // Move to the LOCAL_JOINED state
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(), return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(), s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
@@ -326,9 +328,9 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
throws DbException, FormatException { throws DbException, FormatException {
// If the session has already been aborted, do nothing // If the session has already been aborted, do nothing
if (s.getState() == ERROR) return s; if (s.getState() == ERROR) return s;
// Stop syncing the private group with the contact, if we subscribe // If we subscribe, make the private group invisible to the contact
if (isSubscribedPrivateGroup(txn, s.getPrivateGroupId())) if (isSubscribedPrivateGroup(txn, s.getPrivateGroupId()))
syncPrivateGroupWithContact(txn, s, false); setPrivateGroupVisibility(txn, s, INVISIBLE);
// Send an ABORT message // Send an ABORT message
Message sent = sendAbortMessage(txn, s); Message sent = sendAbortMessage(txn, s);
// Move to the ERROR state // Move to the ERROR state

View File

@@ -29,6 +29,8 @@ import java.util.Map.Entry;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
class TransportPropertyManagerImpl implements TransportPropertyManager, class TransportPropertyManagerImpl implements TransportPropertyManager,
Client, AddContactHook, RemoveContactHook { Client, AddContactHook, RemoveContactHook {
@@ -64,7 +66,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
if (db.containsGroup(txn, g.getId())) return; if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Copy the latest local properties into the group // Copy the latest local properties into the group
Map<TransportId, TransportProperties> local = getLocalProperties(txn); Map<TransportId, TransportProperties> local = getLocalProperties(txn);
for (Entry<TransportId, TransportProperties> e : local.entrySet()) { for (Entry<TransportId, TransportProperties> e : local.entrySet()) {

View File

@@ -87,6 +87,8 @@ import static org.briarproject.api.sharing.SharingConstants.TO_BE_SHARED_BY_US;
import static org.briarproject.api.sharing.SharingConstants.TYPE; import static org.briarproject.api.sharing.SharingConstants.TYPE;
import static org.briarproject.api.sharing.SharingMessage.BaseMessage; import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.sharing.SharingMessage.Invitation; import static org.briarproject.api.sharing.SharingMessage.Invitation;
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ; import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE; import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
@@ -159,7 +161,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
if (db.containsGroup(txn, g.getId())) return; if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(CONTACT_ID, c.getId().getInt()); meta.put(CONTACT_ID, c.getId().getInt());
@@ -908,20 +910,20 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
// TODO we might want to call the add() method of the respective // TODO we might want to call the add() method of the respective
// manager here, because blogs add a description for example // manager here, because blogs add a description for example
db.addGroup(txn, f.getGroup()); db.addGroup(txn, f.getGroup());
db.setVisibleToContact(txn, contactId, f.getId(), true); db.setGroupVisibility(txn, contactId, f.getId(), SHARED);
} else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) { } else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) {
addToList(txn, groupId, TO_BE_SHARED_BY_US, f); addToList(txn, groupId, TO_BE_SHARED_BY_US, f);
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US) { } else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US) {
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f); removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
} else if (task == TASK_SHARE_SHAREABLE) { } else if (task == TASK_SHARE_SHAREABLE) {
db.setVisibleToContact(txn, contactId, f.getId(), true); db.setGroupVisibility(txn, contactId, f.getId(), SHARED);
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f); removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
addToList(txn, groupId, SHARED_BY_US, f); addToList(txn, groupId, SHARED_BY_US, f);
} else if (task == TASK_UNSHARE_SHAREABLE_SHARED_BY_US) { } else if (task == TASK_UNSHARE_SHAREABLE_SHARED_BY_US) {
db.setVisibleToContact(txn, contactId, f.getId(), false); db.setGroupVisibility(txn, contactId, f.getId(), INVISIBLE);
removeFromList(txn, groupId, SHARED_BY_US, f); removeFromList(txn, groupId, SHARED_BY_US, f);
} else if (task == TASK_UNSHARE_SHAREABLE_SHARED_WITH_US) { } else if (task == TASK_UNSHARE_SHAREABLE_SHARED_WITH_US) {
db.setVisibleToContact(txn, contactId, f.getId(), false); db.setGroupVisibility(txn, contactId, f.getId(), INVISIBLE);
removeFromList(txn, groupId, SHARED_WITH_US, f); removeFromList(txn, groupId, SHARED_WITH_US, f);
} }
} }

View File

@@ -49,6 +49,7 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
import static org.briarproject.api.blogs.MessageType.POST; import static org.briarproject.api.blogs.MessageType.POST;
import static org.briarproject.api.identity.Author.Status.VERIFIED; import static org.briarproject.api.identity.Author.Status.VERIFIED;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.blogs.BlogManagerImpl.CLIENT_ID; import static org.briarproject.blogs.BlogManagerImpl.CLIENT_ID;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -102,25 +103,18 @@ public class BlogManagerImplTest extends BriarTestCase {
will(returnValue(blog1.getAuthor())); will(returnValue(blog1.getAuthor()));
oneOf(blogFactory).createBlog(blog1.getAuthor()); oneOf(blogFactory).createBlog(blog1.getAuthor());
will(returnValue(blog1)); will(returnValue(blog1));
oneOf(db).containsGroup(txn, blog1.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, blog1.getGroup()); oneOf(db).addGroup(txn, blog1.getGroup());
oneOf(db).getContacts(txn, blog1.getAuthor().getId());
will(returnValue(contactIds));
oneOf(db).setVisibleToContact(txn, contactId, blog1.getId(), true);
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(contacts)); will(returnValue(contacts));
oneOf(blogFactory).createBlog(blog2.getAuthor()); oneOf(blogFactory).createBlog(blog2.getAuthor());
will(returnValue(blog2)); will(returnValue(blog2));
oneOf(db).containsGroup(txn, blog2.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, blog2.getGroup()); oneOf(db).addGroup(txn, blog2.getGroup());
oneOf(db).setVisibleToContact(txn, contactId, blog2.getId(), true); oneOf(db).setGroupVisibility(txn, contactId, blog2.getId(), SHARED);
oneOf(db).getLocalAuthor(txn, blog1.getAuthor().getId()); oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(blog1.getAuthor())); will(returnValue(blog1.getAuthor()));
oneOf(blogFactory).createBlog(blog1.getAuthor()); oneOf(blogFactory).createBlog(blog1.getAuthor());
will(returnValue(blog1)); will(returnValue(blog1));
oneOf(db).setVisibleToContact(txn, contactId, blog1.getId(), true); oneOf(db).setGroupVisibility(txn, contactId, blog1.getId(), SHARED);
}}); }});
blogManager.createLocalState(txn); blogManager.createLocalState(txn);

View File

@@ -60,6 +60,9 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN; import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
@@ -170,7 +173,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
// removeGroup() // removeGroup()
oneOf(database).containsGroup(txn, groupId); oneOf(database).containsGroup(txn, groupId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).getVisibility(txn, groupId); oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.emptyList())); will(returnValue(Collections.emptyList()));
oneOf(database).removeGroup(txn, groupId); oneOf(database).removeGroup(txn, groupId);
oneOf(eventBus).broadcast(with(any(GroupRemovedEvent.class))); oneOf(eventBus).broadcast(with(any(GroupRemovedEvent.class)));
@@ -267,7 +270,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, true); oneOf(database).addMessage(txn, message, DELIVERED, true);
oneOf(database).mergeMessageMetadata(txn, messageId, metadata); oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).getVisibility(txn, groupId); oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId))); will(returnValue(Collections.singletonList(contactId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId); oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false)); will(returnValue(false));
@@ -403,7 +406,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
transaction = db.startTransaction(false); transaction = db.startTransaction(false);
try { try {
db.isVisibleToContact(transaction, contactId, groupId); db.getGroupVisibility(transaction, contactId, groupId);
fail(); fail();
} catch (NoSuchContactException expected) { } catch (NoSuchContactException expected) {
// Expected // Expected
@@ -487,7 +490,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
transaction = db.startTransaction(false); transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setGroupVisibility(transaction, contactId, groupId, SHARED);
fail(); fail();
} catch (NoSuchContactException expected) { } catch (NoSuchContactException expected) {
// Expected // Expected
@@ -560,14 +563,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the group is in the DB (which it's not) // Check whether the group is in the DB (which it's not)
exactly(9).of(database).startTransaction(); exactly(8).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(9).of(database).containsGroup(txn, groupId); exactly(8).of(database).containsGroup(txn, groupId);
will(returnValue(false)); will(returnValue(false));
exactly(9).of(database).abortTransaction(txn); exactly(8).of(database).abortTransaction(txn);
// This is needed for getMessageStatus(), isVisibleToContact(), and // This is needed for getMessageStatus() and setGroupVisibility()
// setVisibleToContact() to proceed exactly(2).of(database).containsContact(txn, contactId);
exactly(3).of(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -623,16 +625,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(false);
try {
db.isVisibleToContact(transaction, contactId, groupId);
fail();
} catch (NoSuchGroupException expected) {
// Expected
} finally {
db.endTransaction(transaction);
}
transaction = db.startTransaction(false); transaction = db.startTransaction(false);
try { try {
db.mergeGroupMetadata(transaction, groupId, metadata); db.mergeGroupMetadata(transaction, groupId, metadata);
@@ -655,7 +647,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
transaction = db.startTransaction(false); transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setGroupVisibility(transaction, contactId, groupId, SHARED);
fail(); fail();
} catch (NoSuchGroupException expected) { } catch (NoSuchGroupException expected) {
// Expected // Expected
@@ -924,6 +916,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
Transaction transaction = db.startTransaction(false); Transaction transaction = db.startTransaction(false);
try { try {
Ack a = db.generateAck(transaction, contactId, 123); Ack a = db.generateAck(transaction, contactId, 123);
assertNotNull(a);
assertEquals(messagesToAck, a.getMessageIds()); assertEquals(messagesToAck, a.getMessageIds());
db.commitTransaction(transaction); db.commitTransaction(transaction);
} finally { } finally {
@@ -1005,6 +998,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
Transaction transaction = db.startTransaction(false); Transaction transaction = db.startTransaction(false);
try { try {
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency); Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
assertNotNull(o);
assertEquals(ids, o.getMessageIds()); assertEquals(ids, o.getMessageIds());
db.commitTransaction(transaction); db.commitTransaction(transaction);
} finally { } finally {
@@ -1039,6 +1033,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
Transaction transaction = db.startTransaction(false); Transaction transaction = db.startTransaction(false);
try { try {
Request r = db.generateRequest(transaction, contactId, 123); Request r = db.generateRequest(transaction, contactId, 123);
assertNotNull(r);
assertEquals(ids, r.getMessageIds()); assertEquals(ids, r.getMessageIds());
db.commitTransaction(transaction); db.commitTransaction(transaction);
} finally { } finally {
@@ -1139,12 +1134,12 @@ public class DatabaseComponentImplTest extends BriarTestCase {
// First time // First time
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsVisibleGroup(txn, contactId, groupId); oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(true)); will(returnValue(VISIBLE));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, UNKNOWN, false); oneOf(database).addMessage(txn, message, UNKNOWN, false);
oneOf(database).getVisibility(txn, groupId); oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId))); will(returnValue(Collections.singletonList(contactId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId); oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false)); will(returnValue(false));
@@ -1152,8 +1147,8 @@ public class DatabaseComponentImplTest extends BriarTestCase {
// Second time // Second time
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsVisibleGroup(txn, contactId, groupId); oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(true)); will(returnValue(VISIBLE));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).raiseSeenFlag(txn, contactId, messageId); oneOf(database).raiseSeenFlag(txn, contactId, messageId);
@@ -1195,8 +1190,8 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsVisibleGroup(txn, contactId, groupId); oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(true)); will(returnValue(VISIBLE));
// The message wasn't stored but it must still be acked // The message wasn't stored but it must still be acked
oneOf(database).raiseSeenFlag(txn, contactId, messageId); oneOf(database).raiseSeenFlag(txn, contactId, messageId);
oneOf(database).raiseAckFlag(txn, contactId, messageId); oneOf(database).raiseAckFlag(txn, contactId, messageId);
@@ -1230,8 +1225,8 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsVisibleGroup(txn, contactId, groupId); oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(false)); will(returnValue(INVISIBLE));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -1350,9 +1345,9 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsGroup(txn, groupId); oneOf(database).containsGroup(txn, groupId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsVisibleGroup(txn, contactId, groupId); oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(false)); // Not yet visible will(returnValue(INVISIBLE)); // Not yet visible
oneOf(database).addVisibility(txn, contactId, groupId); oneOf(database).addGroupVisibility(txn, contactId, groupId, false);
oneOf(database).getMessageIds(txn, groupId); oneOf(database).getMessageIds(txn, groupId);
will(returnValue(Collections.singletonList(messageId))); will(returnValue(Collections.singletonList(messageId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId); oneOf(database).removeOfferedMessage(txn, contactId, messageId);
@@ -1367,7 +1362,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
Transaction transaction = db.startTransaction(false); Transaction transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setGroupVisibility(transaction, contactId, groupId, VISIBLE);
db.commitTransaction(transaction); db.commitTransaction(transaction);
} finally { } finally {
db.endTransaction(transaction); db.endTransaction(transaction);
@@ -1391,8 +1386,8 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsGroup(txn, groupId); oneOf(database).containsGroup(txn, groupId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsVisibleGroup(txn, contactId, groupId); oneOf(database).getGroupVisibility(txn, contactId, groupId);
will(returnValue(true)); // Already visible will(returnValue(VISIBLE)); // Already visible
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -1400,7 +1395,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
Transaction transaction = db.startTransaction(false); Transaction transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setGroupVisibility(transaction, contactId, groupId, VISIBLE);
db.commitTransaction(transaction); db.commitTransaction(transaction);
} finally { } finally {
db.endTransaction(transaction); db.endTransaction(transaction);
@@ -1666,7 +1661,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, true); oneOf(database).addMessage(txn, message, DELIVERED, true);
oneOf(database).getVisibility(txn, groupId); oneOf(database).getGroupVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId))); will(returnValue(Collections.singletonList(contactId)));
oneOf(database).mergeMessageMetadata(txn, messageId, metadata); oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).removeOfferedMessage(txn, contactId, messageId); oneOf(database).removeOfferedMessage(txn, contactId, messageId);

View File

@@ -45,6 +45,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.api.db.Metadata.REMOVE; import static org.briarproject.api.db.Metadata.REMOVE;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_LENGTH; import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID; import static org.briarproject.api.sync.ValidationManager.State.INVALID;
@@ -171,12 +174,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and a message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true);
// The message has no status yet, so it should not be sendable // The message has no status yet, so it should not be sendable
@@ -209,12 +212,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and an unvalidated message // Add a contact, a shared group and a shared but unvalidated message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, UNKNOWN, true); db.addMessage(txn, message, UNKNOWN, true);
db.addStatus(txn, contactId, messageId, false, false); db.addStatus(txn, contactId, messageId, false, false);
@@ -251,16 +254,68 @@ public class H2DatabaseTest extends BriarTestCase {
} }
@Test @Test
public void testSendableMessagesMustBeShared() throws Exception { public void testSendableMessagesMustHaveSharedGroup() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and an unshared message // Add a contact, an invisible group and a shared message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
// The group is invisible, so the message should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Making the group visible should not make the message sendable
db.addGroupVisibility(txn, contactId, groupId, false);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Sharing the group should make the message sendable
db.setGroupVisibility(txn, contactId, groupId, true);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertEquals(Collections.singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100);
assertEquals(Collections.singletonList(messageId), ids);
// Unsharing the group should make the message unsendable
db.setGroupVisibility(txn, contactId, groupId, false);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Making the group invisible should make the message unsendable
db.removeGroupVisibility(txn, contactId, groupId);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
db.commitTransaction(txn);
db.close();
}
@Test
public void testSendableMessagesMustBeShared() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact, a shared group and an unshared message
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, false); db.addMessage(txn, message, DELIVERED, false);
db.addStatus(txn, contactId, messageId, false, false); db.addStatus(txn, contactId, messageId, false, false);
@@ -287,12 +342,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and a message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false); db.addStatus(txn, contactId, messageId, false, false);
@@ -314,12 +369,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a group // Add a contact and a visible group
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, false);
// Add some messages to ack // Add some messages to ack
MessageId messageId1 = new MessageId(TestUtils.getRandomId()); MessageId messageId1 = new MessageId(TestUtils.getRandomId());
@@ -351,12 +406,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and a message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false); db.addStatus(txn, contactId, messageId, false, false);
@@ -515,12 +570,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a group // Add a contact and a shared group
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, true);
// The message is not in the database // The message is not in the database
assertFalse(db.containsVisibleMessage(txn, contactId, messageId)); assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
@@ -569,7 +624,7 @@ public class H2DatabaseTest extends BriarTestCase {
} }
@Test @Test
public void testVisibility() throws Exception { public void testGroupVisibility() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -580,52 +635,33 @@ public class H2DatabaseTest extends BriarTestCase {
db.addGroup(txn, group); db.addGroup(txn, group);
// The group should not be visible to the contact // The group should not be visible to the contact
assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.emptyList(),
db.getGroupVisibility(txn, groupId));
// Make the group visible to the contact // Make the group visible to the contact
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, false);
assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId), assertEquals(Collections.singletonList(contactId),
db.getVisibility(txn, groupId)); db.getGroupVisibility(txn, groupId));
// Share the group with the contact
db.setGroupVisibility(txn, contactId, groupId, true);
assertEquals(SHARED, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId),
db.getGroupVisibility(txn, groupId));
// Unshare the group with the contact
db.setGroupVisibility(txn, contactId, groupId, false);
assertEquals(VISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.singletonList(contactId),
db.getGroupVisibility(txn, groupId));
// Make the group invisible again // Make the group invisible again
db.removeVisibility(txn, contactId, groupId); db.removeGroupVisibility(txn, contactId, groupId);
assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
assertEquals(Collections.emptyList(),
db.commitTransaction(txn); db.getGroupVisibility(txn, groupId));
db.close();
}
@Test
public void testMultipleGroupChanges() throws Exception {
// Create some groups
List<Group> groups = new ArrayList<>();
for (int i = 0; i < 100; i++) {
GroupId id = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(TestUtils.getRandomString(5));
byte[] descriptor = new byte[0];
groups.add(new Group(id, clientId, descriptor));
}
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact and the groups
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true));
for (Group g : groups) db.addGroup(txn, g);
// Make the groups visible to the contact
Collections.shuffle(groups);
for (Group g : groups) db.addVisibility(txn, contactId, g.getId());
// Make some of the groups invisible to the contact and remove them all
Collections.shuffle(groups);
for (Group g : groups) {
if (Math.random() < 0.5)
db.removeVisibility(txn, contactId, g.getId());
db.removeGroup(txn, g.getId());
}
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -1144,15 +1180,15 @@ public class H2DatabaseTest extends BriarTestCase {
metadata1.put("foo", new byte[]{'b', 'a', 'r'}); metadata1.put("foo", new byte[]{'b', 'a', 'r'});
db.mergeMessageMetadata(txn, messageId1, metadata1); db.mergeMessageMetadata(txn, messageId1, metadata1);
for (int i = 1; i <= 2; i++) { for (int i = 0; i < 2; i++) {
Metadata query; Metadata query;
if (i == 1) { if (i == 0) {
// Query the metadata with an empty query // Query the metadata with an empty query
query = new Metadata(); query = new Metadata();
} else { } else {
// Query for foo // Query for foo
query = new Metadata(); query = new Metadata();
query.put("foo", metadata.get("foo")); query.put("foo", new byte[]{'b', 'a', 'r'});
} }
db.setMessageState(txn, messageId, DELIVERED); db.setMessageState(txn, messageId, DELIVERED);
@@ -1160,32 +1196,39 @@ public class H2DatabaseTest extends BriarTestCase {
Map<MessageId, Metadata> all = Map<MessageId, Metadata> all =
db.getMessageMetadata(txn, groupId, query); db.getMessageMetadata(txn, groupId, query);
assertEquals(2, all.size()); assertEquals(2, all.size());
assertEquals(2, all.get(messageId).size()); assertMetadataEquals(metadata, all.get(messageId));
assertEquals(1, all.get(messageId1).size()); assertMetadataEquals(metadata1, all.get(messageId1));
// No metadata for unknown messages // No metadata for unknown messages
db.setMessageState(txn, messageId, UNKNOWN); db.setMessageState(txn, messageId, UNKNOWN);
db.setMessageState(txn, messageId1, UNKNOWN);
all = db.getMessageMetadata(txn, groupId, query); all = db.getMessageMetadata(txn, groupId, query);
assertTrue(all.isEmpty()); assertEquals(1, all.size());
assertMetadataEquals(metadata1, all.get(messageId1));
// No metadata for invalid messages // No metadata for invalid messages
db.setMessageState(txn, messageId, INVALID); db.setMessageState(txn, messageId, INVALID);
db.setMessageState(txn, messageId1, INVALID);
all = db.getMessageMetadata(txn, groupId, query); all = db.getMessageMetadata(txn, groupId, query);
assertTrue(all.isEmpty()); assertEquals(1, all.size());
assertMetadataEquals(metadata1, all.get(messageId1));
// No metadata for pending messages // No metadata for pending messages
db.setMessageState(txn, messageId, PENDING); db.setMessageState(txn, messageId, PENDING);
db.setMessageState(txn, messageId1, PENDING);
all = db.getMessageMetadata(txn, groupId, query); all = db.getMessageMetadata(txn, groupId, query);
assertTrue(all.isEmpty()); assertEquals(1, all.size());
assertMetadataEquals(metadata1, all.get(messageId1));
} }
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
} }
private void assertMetadataEquals(Metadata m1, Metadata m2) {
assertEquals(m1.keySet(), m2.keySet());
for (Entry<String, byte[]> e : m1.entrySet()) {
assertArrayEquals(e.getValue(), m2.get(e.getKey()));
}
}
@Test @Test
public void testMessageDependencies() throws Exception { public void testMessageDependencies() throws Exception {
MessageId messageId1 = new MessageId(TestUtils.getRandomId()); MessageId messageId1 = new MessageId(TestUtils.getRandomId());
@@ -1398,16 +1441,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
// Add a group and make it visible to the contact
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, true);
// Add a message to the group
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false); db.addStatus(txn, contactId, messageId, false, false);
@@ -1471,33 +1510,7 @@ public class H2DatabaseTest extends BriarTestCase {
} }
@Test @Test
public void testGroupsVisibleToContacts() throws Exception { public void testDifferentLocalAuthorsCanHaveTheSameContact()
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact and a group
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true));
db.addGroup(txn, group);
// The group should not be visible to the contact
assertFalse(db.containsVisibleGroup(txn, contactId, groupId));
// Make the group visible to the contact
db.addVisibility(txn, contactId, groupId);
assertTrue(db.containsVisibleGroup(txn, contactId, groupId));
// Make the group invisible to the contact
db.removeVisibility(txn, contactId, groupId);
assertFalse(db.containsVisibleGroup(txn, contactId, groupId));
db.commitTransaction(txn);
db.close();
}
@Test
public void testDifferentLocalPseudonymsCanHaveTheSameContact()
throws Exception { throws Exception {
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId()); AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
LocalAuthor localAuthor1 = new LocalAuthor(localAuthorId1, "Carol", LocalAuthor localAuthor1 = new LocalAuthor(localAuthorId1, "Carol",
@@ -1506,11 +1519,11 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add two local pseudonyms // Add two local authors
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
db.addLocalAuthor(txn, localAuthor1); db.addLocalAuthor(txn, localAuthor1);
// Add the same contact for each local pseudonym // Add the same contact for each local author
ContactId contactId = ContactId contactId =
db.addContact(txn, author, localAuthorId, true, true); db.addContact(txn, author, localAuthorId, true, true);
ContactId contactId1 = ContactId contactId1 =
@@ -1531,12 +1544,12 @@ public class H2DatabaseTest extends BriarTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and a message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId, assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true, true)); true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true); db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false); db.addStatus(txn, contactId, messageId, false, false);