diff --git a/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java b/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java index f049c142a86f0b4a1ac192ec10525432d51e2e46..259be1a9d4f5295d7ab36be98822125757cae24e 100644 --- a/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java +++ b/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java @@ -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 diff --git a/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java b/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java index 10cb986029898234208124b94e440b08e26267c0..623a1ed375adf8d37984d83208765693312e35c1 100644 --- a/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java +++ b/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java @@ -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) {