diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionManager.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionManager.java index 84b4b3028..a3bc6f007 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionManager.java @@ -29,6 +29,12 @@ public interface IntroductionManager extends ConversationClient { */ boolean canIntroduce(Contact c1, Contact c2) throws DbException; + /** + * Returns true if both contacts can be introduced at this moment. + */ + boolean canIntroduce(Transaction txn, Contact c1, Contact c2) + throws DbException; + /** * The current minor version of the introduction client. */ @@ -40,6 +46,12 @@ public interface IntroductionManager extends ConversationClient { void makeIntroduction(Contact c1, Contact c2, @Nullable String text) throws DbException; + /** + * Sends two initial introduction messages. + */ + void makeIntroduction(Transaction txn, Contact c1, Contact c2, + @Nullable String text) throws DbException; + /** * Responds to an introduction. */ diff --git a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java index ceeb8d9c6..756231bd8 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java @@ -52,11 +52,21 @@ public interface PrivateGroupManager { void addPrivateGroup(Transaction txn, PrivateGroup group, GroupMessage joinMsg, boolean creator) throws DbException; + /** + * Removes a dissolved private group. + */ + void removePrivateGroup(Transaction txn, GroupId g) throws DbException; + /** * Removes a dissolved private group. */ void removePrivateGroup(GroupId g) throws DbException; + /** + * Returns the ID of the user's previous message sent to the group + */ + MessageId getPreviousMsgId(Transaction txn, GroupId g) throws DbException; + /** * Returns the ID of the user's previous message sent to the group */ @@ -112,7 +122,8 @@ public interface PrivateGroupManager { /** * Returns true if the given private group was created by us. */ - boolean isOurPrivateGroup(Transaction txn, PrivateGroup g) throws DbException; + boolean isOurPrivateGroup(Transaction txn, PrivateGroup g) + throws DbException; /** * Returns the text of the private group message with the given ID. @@ -161,6 +172,12 @@ public interface PrivateGroupManager { */ GroupCount getGroupCount(GroupId g) throws DbException; + /** + * Marks a message as read or unread and updates the group count. + */ + void setReadFlag(Transaction txn, GroupId g, MessageId m, boolean read) + throws DbException; + /** * Marks a message as read or unread and updates the group count. */ diff --git a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/invitation/GroupInvitationManager.java b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/invitation/GroupInvitationManager.java index dcff16b56..4efd89f76 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/invitation/GroupInvitationManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/invitation/GroupInvitationManager.java @@ -3,6 +3,7 @@ package org.briarproject.briar.api.privategroup.invitation; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.api.client.ProtocolStateException; @@ -47,6 +48,18 @@ public interface GroupInvitationManager extends ConversationClient { long timestamp, byte[] signature, long autoDeleteTimer) throws DbException; + /** + * Sends an invitation to share the given private group with the given + * contact, including an optional message. + * + * @throws ProtocolStateException if the group is no longer eligible to be + * shared with the contact, for example because an invitation is already + * pending. + */ + void sendInvitation(Transaction txn, GroupId g, ContactId c, + @Nullable String text, long timestamp, byte[] signature, + long autoDeleteTimer) throws DbException; + /** * Responds to a pending private group invitation from the given contact. * @@ -56,6 +69,15 @@ public interface GroupInvitationManager extends ConversationClient { void respondToInvitation(ContactId c, PrivateGroup g, boolean accept) throws DbException; + /** + * Responds to a pending private group invitation from the given contact. + * + * @throws ProtocolStateException if the invitation is no longer pending, + * for example because the group has been dissolved. + */ + void respondToInvitation(Transaction txn, ContactId c, PrivateGroup g, + boolean accept) throws DbException; + /** * Responds to a pending private group invitation from the given contact. * @@ -65,6 +87,15 @@ public interface GroupInvitationManager extends ConversationClient { void respondToInvitation(ContactId c, SessionId s, boolean accept) throws DbException; + /** + * Responds to a pending private group invitation from the given contact. + * + * @throws ProtocolStateException if the invitation is no longer pending, + * for example because the group has been dissolved. + */ + void respondToInvitation(Transaction txn, ContactId c, SessionId s, + boolean accept) throws DbException; + /** * Makes the user's relationship with the given contact visible to the * given private group. @@ -74,11 +105,27 @@ public interface GroupInvitationManager extends ConversationClient { */ void revealRelationship(ContactId c, GroupId g) throws DbException; + /** + * Makes the user's relationship with the given contact visible to the + * given private group. + * + * @throws ProtocolStateException if the relationship is no longer eligible + * to be revealed, for example because the contact has revealed it. + */ + void revealRelationship(Transaction txn, ContactId c, GroupId g) + throws DbException; + /** * Returns all private groups to which the user has been invited. */ Collection getInvitations() throws DbException; + /** + * Returns all private groups to which the user has been invited. + */ + Collection getInvitations(Transaction txn) + throws DbException; + /** * Returns the current {@link SharingStatus} for the given {@link Contact} * and {@link PrivateGroup} identified by the given {@link GroupId}. @@ -89,4 +136,16 @@ public interface GroupInvitationManager extends ConversationClient { * was already dissolved. */ SharingStatus getSharingStatus(Contact c, GroupId g) throws DbException; + + /** + * Returns the current {@link SharingStatus} for the given {@link Contact} + * and {@link PrivateGroup} identified by the given {@link GroupId}. + * This indicates whether the {@link PrivateGroup} can be shared + * with the contact. + * + * @throws ProtocolStateException if {@link PrivateGroup} + * was already dissolved. + */ + SharingStatus getSharingStatus(Transaction txn, Contact c, GroupId g) + throws DbException; } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java index b3b1a6077..e3bfb9943 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java @@ -300,36 +300,37 @@ class IntroductionManagerImpl extends ConversationClientImpl @Override public boolean canIntroduce(Contact c1, Contact c2) throws DbException { - Transaction txn = db.startTransaction(true); + return db.transactionWithResult(true, + txn -> canIntroduce(txn, c1, c2)); + } + + public boolean canIntroduce(Transaction txn, Contact c1, Contact c2) + throws DbException { try { - boolean can = canIntroduce(txn, c1, c2); - db.commitTransaction(txn); - return can; + // Look up the session, if there is one + Author introducer = identityManager.getLocalAuthor(txn); + SessionId sessionId = + crypto.getSessionId(introducer, c1.getAuthor(), + c2.getAuthor()); + StoredSession ss = getSession(txn, sessionId); + if (ss == null) return true; + IntroducerSession session = + sessionParser.parseIntroducerSession(ss.bdfSession); + return session.getState().isComplete(); } catch (FormatException e) { throw new DbException(e); - } finally { - db.endTransaction(txn); } } - private boolean canIntroduce(Transaction txn, Contact c1, Contact c2) - throws DbException, FormatException { - // Look up the session, if there is one - Author introducer = identityManager.getLocalAuthor(txn); - SessionId sessionId = - crypto.getSessionId(introducer, c1.getAuthor(), - c2.getAuthor()); - StoredSession ss = getSession(txn, sessionId); - if (ss == null) return true; - IntroducerSession session = - sessionParser.parseIntroducerSession(ss.bdfSession); - return session.getState().isComplete(); + public void makeIntroduction(Contact c1, Contact c2, @Nullable String text) + throws DbException { + db.transaction(false, + txn -> makeIntroduction(txn, c1, c2, text)); } @Override - public void makeIntroduction(Contact c1, Contact c2, @Nullable String text) - throws DbException { - Transaction txn = db.startTransaction(false); + public void makeIntroduction(Transaction txn, Contact c1, Contact c2, + @Nullable String text) throws DbException { try { // Look up the session, if there is one Author introducer = identityManager.getLocalAuthor(txn); @@ -363,11 +364,8 @@ class IntroductionManagerImpl extends ConversationClientImpl session = introducerEngine.onRequestAction(txn, session, text); // Store the updated session storeSession(txn, storageId, session); - db.commitTransaction(txn); } catch (FormatException e) { throw new DbException(e); - } finally { - db.endTransaction(txn); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java index 088cbc4ce..1e06214ca 100644 --- a/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java @@ -150,40 +150,35 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook } @Override - public void removePrivateGroup(GroupId g) throws DbException { - Transaction txn = db.startTransaction(false); - try { - for (PrivateGroupHook hook : hooks) { - hook.removingGroup(txn, g); - } - Group group = db.getGroup(txn, g); - db.removeGroup(txn, group); - db.commitTransaction(txn); - } finally { - db.endTransaction(txn); + public void removePrivateGroup(Transaction txn, GroupId g) + throws DbException { + for (PrivateGroupHook hook : hooks) { + hook.removingGroup(txn, g); } + Group group = db.getGroup(txn, g); + db.removeGroup(txn, group); + } + + @Override + public void removePrivateGroup(GroupId g) throws DbException { + db.transaction(false, txn -> removePrivateGroup(txn, g)); } @Override public MessageId getPreviousMsgId(GroupId g) throws DbException { - MessageId previousMsgId; - Transaction txn = db.startTransaction(true); - try { - previousMsgId = getPreviousMsgId(txn, g); - db.commitTransaction(txn); - } catch (FormatException e) { - throw new DbException(e); - } finally { - db.endTransaction(txn); - } - return previousMsgId; + return db.transactionWithResult(true, + txn -> getPreviousMsgId(txn, g)); } - private MessageId getPreviousMsgId(Transaction txn, GroupId g) - throws DbException, FormatException { - BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g); - byte[] previousMsgIdBytes = d.getRaw(KEY_PREVIOUS_MSG_ID); - return new MessageId(previousMsgIdBytes); + public MessageId getPreviousMsgId(Transaction txn, GroupId g) + throws DbException { + try { + BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g); + byte[] previousMsgIdBytes = d.getRaw(KEY_PREVIOUS_MSG_ID); + return new MessageId(previousMsgIdBytes); + } catch (FormatException e) { + throw new DbException(e); + } } private void setPreviousMsgId(Transaction txn, GroupId g, @@ -483,11 +478,16 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook return messageTracker.getGroupCount(g); } + @Override + public void setReadFlag(Transaction txn, GroupId g, MessageId m, + boolean read) throws DbException { + messageTracker.setReadFlag(txn, g, m, read); + } + @Override public void setReadFlag(GroupId g, MessageId m, boolean read) throws DbException { - db.transaction(false, txn -> - messageTracker.setReadFlag(txn, g, m, read)); + db.transaction(false, txn -> setReadFlag(txn, g, m, read)); } @Override diff --git a/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImpl.java index 26742b104..3d1263815 100644 --- a/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImpl.java @@ -319,8 +319,16 @@ class GroupInvitationManagerImpl extends ConversationClientImpl public void sendInvitation(GroupId privateGroupId, ContactId c, @Nullable String text, long timestamp, byte[] signature, long autoDeleteTimer) throws DbException { + db.transaction(false, + txn -> sendInvitation(txn, privateGroupId, c, text, timestamp, + signature, autoDeleteTimer)); + } + + @Override + public void sendInvitation(Transaction txn, GroupId privateGroupId, + ContactId c, @Nullable String text, long timestamp, + byte[] signature, long autoDeleteTimer) throws DbException { SessionId sessionId = getSessionId(privateGroupId); - Transaction txn = db.startTransaction(false); try { // Look up the session, if there is one Contact contact = db.getContact(txn, c); @@ -344,11 +352,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl timestamp, signature, autoDeleteTimer); // Store the updated session storeSession(txn, storageId, session); - db.commitTransaction(txn); } catch (FormatException e) { throw new DbException(e); - } finally { - db.endTransaction(txn); } } @@ -358,6 +363,12 @@ class GroupInvitationManagerImpl extends ConversationClientImpl respondToInvitation(c, getSessionId(g.getId()), accept); } + @Override + public void respondToInvitation(Transaction txn, ContactId c, + PrivateGroup g, boolean accept) throws DbException { + respondToInvitation(txn, c, getSessionId(g.getId()), accept); + } + @Override public void respondToInvitation(ContactId c, SessionId sessionId, boolean accept) throws DbException { @@ -365,6 +376,12 @@ class GroupInvitationManagerImpl extends ConversationClientImpl txn -> respondToInvitation(txn, c, sessionId, accept, false)); } + @Override + public void respondToInvitation(Transaction txn, ContactId c, + SessionId sessionId, boolean accept) throws DbException { + respondToInvitation(txn, c, sessionId, accept, false); + } + private void respondToInvitation(Transaction txn, ContactId c, SessionId sessionId, boolean accept, boolean isAutoDecline) throws DbException { @@ -390,7 +407,12 @@ class GroupInvitationManagerImpl extends ConversationClientImpl @Override public void revealRelationship(ContactId c, GroupId g) throws DbException { - Transaction txn = db.startTransaction(false); + db.transaction(false, txn -> revealRelationship(txn, c, g)); + } + + @Override + public void revealRelationship(Transaction txn, ContactId c, GroupId g) + throws DbException { try { // Look up the session Contact contact = db.getContact(txn, c); @@ -404,11 +426,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl session = peerEngine.onJoinAction(txn, session); // Store the updated session storeSession(txn, ss.storageId, session); - db.commitTransaction(txn); } catch (FormatException e) { throw new DbException(e); - } finally { - db.endTransaction(txn); } } @@ -495,9 +514,14 @@ class GroupInvitationManagerImpl extends ConversationClientImpl @Override public Collection getInvitations() throws DbException { + return db.transactionWithResult(true, this::getInvitations); + } + + @Override + public Collection getInvitations(Transaction txn) + throws DbException { List items = new ArrayList<>(); BdfDictionary query = messageParser.getInvitesAvailableToAnswerQuery(); - Transaction txn = db.startTransaction(true); try { // Look up the available invite messages for each contact for (Contact c : db.getContacts(txn)) { @@ -507,11 +531,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl for (MessageId m : results) items.add(parseGroupInvitationItem(txn, c, m)); } - db.commitTransaction(txn); } catch (FormatException e) { throw new DbException(e); - } finally { - db.endTransaction(txn); } return items; } @@ -519,15 +540,20 @@ class GroupInvitationManagerImpl extends ConversationClientImpl @Override public SharingStatus getSharingStatus(Contact c, GroupId privateGroupId) throws DbException { + return db.transactionWithResult(true, + txn -> getSharingStatus(txn, c, privateGroupId)); + } + + @Override + public SharingStatus getSharingStatus(Transaction txn, Contact c, + GroupId privateGroupId) throws DbException { GroupId contactGroupId = getContactGroup(c).getId(); SessionId sessionId = getSessionId(privateGroupId); - Transaction txn = db.startTransaction(true); try { Visibility client = clientVersioningManager.getClientVisibility(txn, c.getId(), PrivateGroupManager.CLIENT_ID, PrivateGroupManager.MAJOR_VERSION); StoredSession ss = getSession(txn, contactGroupId, sessionId); - db.commitTransaction(txn); // The group can't be shared unless the contact supports the client if (client != SHARED) return SharingStatus.NOT_SUPPORTED; // If there's no session, the contact can be invited @@ -548,8 +574,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl throw new AssertionError("Unhandled state: " + state.name()); } catch (FormatException e) { throw new DbException(e); - } finally { - db.endTransaction(txn); } } diff --git a/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImplTest.java index 8f3c2398c..9e8f07b8e 100644 --- a/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImplTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/privategroup/invitation/GroupInvitationManagerImplTest.java @@ -570,9 +570,8 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { byte[] signature = getRandomBytes(42); expectGetSession(noResults, sessionId, contactGroup.getId()); - context.checking(new Expectations() {{ - oneOf(db).startTransaction(false); - will(returnValue(txn)); + context.checking(new DbExpectations() {{ + oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).getContact(txn, contactId); will(returnValue(contact)); oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, @@ -587,10 +586,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { will(returnValue(creatorSession)); }}); expectStoreSession(creatorSession, storageMessage.getId()); - context.checking(new Expectations() {{ - oneOf(db).commitTransaction(txn); - oneOf(db).endTransaction(txn); - }}); groupInvitationManager.sendInvitation(privateGroup.getId(), contactId, text, time, signature, NO_AUTO_DELETE_TIMER); } @@ -602,9 +597,8 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { byte[] signature = getRandomBytes(43); expectGetSession(oneResult, sessionId, contactGroup.getId()); - context.checking(new Expectations() {{ - oneOf(db).startTransaction(false); - will(returnValue(txn)); + context.checking(new DbExpectations() {{ + oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).getContact(txn, contactId); will(returnValue(contact)); oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, @@ -619,10 +613,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { will(returnValue(creatorSession)); }}); expectStoreSession(creatorSession, storageMessage.getId()); - context.checking(new Expectations() {{ - oneOf(db).commitTransaction(txn); - oneOf(db).endTransaction(txn); - }}); groupInvitationManager.sendInvitation(privateGroup.getId(), contactId, text, time, signature, NO_AUTO_DELETE_TIMER); } @@ -700,9 +690,8 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { @Test public void testRevealRelationship() throws Exception { - context.checking(new Expectations() {{ - oneOf(db).startTransaction(false); - will(returnValue(txn)); + context.checking(new DbExpectations() {{ + oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).getContact(txn, contactId); will(returnValue(contact)); oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, @@ -713,8 +702,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { will(returnValue(peerSession)); oneOf(peerEngine).onJoinAction(txn, peerSession); will(returnValue(peerSession)); - oneOf(db).commitTransaction(txn); - oneOf(db).endTransaction(txn); }}); expectGetSession(oneResult, sessionId, contactGroup.getId()); expectStoreSession(peerSession, storageMessage.getId()); @@ -725,15 +712,13 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { @Test(expected = IllegalArgumentException.class) public void testRevealRelationshipWithoutSession() throws Exception { - context.checking(new Expectations() {{ - oneOf(db).startTransaction(false); - will(returnValue(txn)); + context.checking(new DbExpectations() {{ + oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).getContact(txn, contactId); will(returnValue(contact)); oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, MAJOR_VERSION, contact); will(returnValue(contactGroup)); - oneOf(db).endTransaction(txn); }}); expectGetSession(noResults, sessionId, contactGroup.getId()); @@ -832,11 +817,10 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { PrivateGroup pg = new PrivateGroup(group, groupName, author, salt); - context.checking(new Expectations() {{ + context.checking(new DbExpectations() {{ oneOf(messageParser).getInvitesAvailableToAnswerQuery(); will(returnValue(query)); - oneOf(db).startTransaction(true); - will(returnValue(txn)); + oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).getContacts(txn); will(returnValue(singletonList(contact))); oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, @@ -856,9 +840,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { oneOf(privateGroupFactory).createPrivateGroup(groupName, author, salt); will(returnValue(pg)); - // end transaction - oneOf(db).commitTransaction(txn); - oneOf(db).endTransaction(txn); }}); Collection items = @@ -910,12 +891,11 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { private void expectIsInvitationAllowed(CreatorState state) throws Exception { expectGetSession(oneResult, sessionId, contactGroup.getId()); - context.checking(new Expectations() {{ + context.checking(new DbExpectations() {{ oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, MAJOR_VERSION, contact); will(returnValue(contactGroup)); - oneOf(db).startTransaction(true); - will(returnValue(txn)); + oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(clientVersioningManager).getClientVisibility(txn, contactId, PrivateGroupManager.CLIENT_ID, PrivateGroupManager.MAJOR_VERSION); @@ -925,8 +905,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase { will(returnValue(creatorSession)); oneOf(creatorSession).getState(); will(returnValue(state)); - oneOf(db).commitTransaction(txn); - oneOf(db).endTransaction(txn); }}); }