Merge branch '372-clean-up-introduction-session-states-when-removing-contact' into 'master'

Clean up Introduction Session States

...for introducer when both introducees have been deleted.

It can't be deleted when only one introducee is removed, because then all messages in the private conversation with the other introducee will disappear, because their corresponding session state can't be found anymore.

Closes #372

See merge request !189
This commit is contained in:
Torsten Grote
2016-05-16 13:18:57 +00:00
2 changed files with 144 additions and 13 deletions

View File

@@ -832,6 +832,106 @@ public class IntroductionIntegrationTest extends BriarTestCase {
}
}
@Test
public void testIntroduceesRemovedCleanup() throws Exception {
startLifecycles();
try {
// Add Identities
addDefaultIdentities();
// Add Transport Properties
addTransportProperties();
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
);
assertTrue(contactId0.equals(contactId02));
// listen to events
IntroducerListener listener0 = new IntroducerListener();
t0.getEventBus().addListener(listener0);
IntroduceeListener listener1 = new IntroduceeListener(1, true);
t1.getEventBus().addListener(listener1);
IntroduceeListener listener2 = new IntroduceeListener(2, true);
t2.getEventBus().addListener(listener2);
// make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contactManager0.getContact(contactId1);
Contact introducee2 = contactManager0.getContact(contactId2);
introductionManager0
.makeIntroduction(introducee1, introducee2, "Hi!", time);
// sync first request message
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// get database and local group for introducee
DatabaseComponent db0 = t0.getDatabaseComponent();
IntroductionGroupFactory groupFactory0 =
t0.getIntroductionGroupFactory();
Group group1 = groupFactory0.createLocalGroup();
// get local session state messages
Map<MessageId, Metadata> map;
Transaction txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
txn.setComplete();
} finally {
db0.endTransaction(txn);
}
// check that we have one session state
assertEquals(1, map.size());
// introducer removes introducee1
contactManager0.removeContact(contactId1);
// get local session state messages again
txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
txn.setComplete();
} finally {
db0.endTransaction(txn);
}
// make sure local state is still there
assertEquals(1, map.size());
// introducer removes other introducee
contactManager0.removeContact(contactId2);
// get local session state messages again
txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
txn.setComplete();
} finally {
db0.endTransaction(txn);
}
// make sure local state is gone now
assertEquals(0, map.size());
} finally {
stopLifecycles();
}
}
// TODO add a test for faking responses when #256 is implemented
@After

View File

@@ -9,10 +9,12 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager.AddContactHook;
import org.briarproject.api.contact.ContactManager.RemoveContactHook;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchContactException;
import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.AuthorId;
@@ -136,29 +138,58 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
// check for open sessions with that contact and abort those
Long id = (long) c.getId().getInt();
GroupId gId = introductionGroupFactory.createLocalGroup().getId();
// search for session states where c introduced us
BdfDictionary query = BdfDictionary.of(
new BdfEntry(ROLE, ROLE_INTRODUCEE),
new BdfEntry(CONTACT_ID_1, c.getId().getInt())
);
try {
Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn,
introductionGroupFactory.createLocalGroup().getId());
.getMessageMetadataAsDictionary(txn, gId, query);
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
// delete states if introducee removes introducer
deleteMessage(txn, entry.getKey());
}
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
// check for open sessions with c and abort those,
// so the other introducee knows
query = BdfDictionary.of(
new BdfEntry(ROLE, ROLE_INTRODUCER)
);
try {
Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, gId, query);
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
BdfDictionary d = entry.getValue();
long role = d.getLong(ROLE, -1L);
if (role != ROLE_INTRODUCER) {
if (d.getLong(CONTACT_ID_1).equals(id)) {
// delete states if introducee removes introducer
deleteMessage(txn, entry.getKey());
}
}
else if (d.getLong(CONTACT_ID_1).equals(id) ||
d.getLong(CONTACT_ID_2).equals(id)) {
ContactId c1 = new ContactId(d.getLong(CONTACT_ID_1).intValue());
ContactId c2 = new ContactId(d.getLong(CONTACT_ID_2).intValue());
if (c1.equals(c.getId()) || c2.equals(c.getId())) {
IntroducerProtocolState state = IntroducerProtocolState
.fromValue(d.getLong(STATE).intValue());
// abort protocol if still ongoing
if (IntroducerProtocolState.isOngoing(state)) {
introducerManager.abort(txn, d);
}
// also delete state if both contacts have been deleted
if (c1.equals(c.getId())) {
try {
db.getContact(txn, c2);
} catch (NoSuchContactException e) {
deleteMessage(txn, entry.getKey());
}
} else if (c2.equals(c.getId())) {
try {
db.getContact(txn, c1);
} catch (NoSuchContactException e) {
deleteMessage(txn, entry.getKey());
}
}
}
}
} catch (FormatException e) {