diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml index 2feaa4e95fbea9bbb14af009cc80761f3beb8141..a7a72e1fbf12397bf9c5d07f34013c1654134e9e 100644 --- a/.idea/codeStyleSettings.xml +++ b/.idea/codeStyleSettings.xml @@ -31,6 +31,8 @@ </value> </option> <option name="RIGHT_MARGIN" value="100" /> + <option name="JD_ALIGN_PARAM_COMMENTS" value="false" /> + <option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" /> <AndroidXmlCodeStyleSettings> <option name="USE_CUSTOM_SETTINGS" value="true" /> </AndroidXmlCodeStyleSettings> diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationManager.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationManager.java index 489e51a5917bc5f477cef44bd6ff2d71d82d9218..59475c9749bcf73768ae2cfef84a164ce0b67d1e 100644 --- a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationManager.java +++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationManager.java @@ -1,5 +1,6 @@ package org.briarproject.api.privategroup.invitation; +import org.briarproject.api.clients.ProtocolStateException; import org.briarproject.api.clients.SessionId; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; @@ -26,22 +27,41 @@ public interface GroupInvitationManager { /** * 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(GroupId g, ContactId c, @Nullable String message, long timestamp, byte[] signature) 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(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(ContactId c, SessionId s, boolean accept) 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(ContactId c, GroupId g) throws DbException; + /** * Returns all private group invitation messages related to the given * contact. diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java index c2bdf6cf493060e57de864e01fbb16c985c5150a..5ff1193f93ed4f4534f32d99a78a77b4908aa2f4 100644 --- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java +++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java @@ -443,21 +443,26 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g); BdfList members = meta.getList(GROUP_KEY_MEMBERS); Visibility v = INVISIBLE; - boolean foundMember = false; + boolean foundMember = false, changed = false; for (int i = 0 ; i < members.size(); i++) { BdfDictionary d = members.getDictionary(i); AuthorId memberId = new AuthorId(d.getRaw(KEY_MEMBER_ID)); if (a.equals(memberId)) { foundMember = true; - Visibility vOld = getVisibility(d); - if (vOld != INVISIBLE) throw new ProtocolStateException(); - v = byContact ? REVEALED_BY_CONTACT : REVEALED_BY_US; - d.put(GROUP_KEY_VISIBILITY, v.getInt()); + // Don't update the visibility if the contact is already visible + if (getVisibility(d) == INVISIBLE) { + changed = true; + v = byContact ? REVEALED_BY_CONTACT : REVEALED_BY_US; + d.put(GROUP_KEY_VISIBILITY, v.getInt()); + } + break; } } if (!foundMember) throw new ProtocolStateException(); - clientHelper.mergeGroupMetadata(txn, g, meta); - txn.attach(new ContactRelationshipRevealedEvent(g, v)); + if (changed) { + clientHelper.mergeGroupMetadata(txn, g, meta); + txn.attach(new ContactRelationshipRevealedEvent(g, v)); + } } @Override diff --git a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationManagerImpl.java b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationManagerImpl.java index ae11b48b850f13154046c447614061e910e8dd4c..d4a51798f1a77e512aee6355681d660eaf722e1c 100644 --- a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationManagerImpl.java +++ b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationManagerImpl.java @@ -315,6 +315,30 @@ class GroupInvitationManagerImpl extends ConversationClientImpl } } + @Override + public void revealRelationship(ContactId c, GroupId g) throws DbException { + Transaction txn = db.startTransaction(false); + try { + // Look up the session + Contact contact = db.getContact(txn, c); + GroupId contactGroupId = getContactGroup(contact).getId(); + StoredSession ss = getSession(txn, contactGroupId, getSessionId(g)); + if (ss == null) throw new IllegalArgumentException(); + // Parse the session + PeerSession session = sessionParser + .parsePeerSession(contactGroupId, ss.bdfSession); + // Handle the join action + 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); + } + } + private <S extends Session> S handleAction(Transaction txn, LocalAction type, S session, ProtocolEngine<S> engine) throws DbException, FormatException { @@ -435,9 +459,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl db.commitTransaction(txn); // If there's no session, the contact can be invited if (ss == null) return true; - // If there's a session, it should be a creator session - if (sessionParser.getRole(ss.bdfSession) != CREATOR) - throw new IllegalArgumentException(); // If the session's in the start state, the contact can be invited CreatorSession session = sessionParser .parseCreatorSession(contactGroupId, ss.bdfSession); diff --git a/briar-core/src/org/briarproject/privategroup/invitation/PeerProtocolEngine.java b/briar-core/src/org/briarproject/privategroup/invitation/PeerProtocolEngine.java index 6d3c721f38fea70a2a25c5396abc0e0f54bce442..84aa82ced367d97dd8f063eeed21aaa34a0b37dd 100644 --- a/briar-core/src/org/briarproject/privategroup/invitation/PeerProtocolEngine.java +++ b/briar-core/src/org/briarproject/privategroup/invitation/PeerProtocolEngine.java @@ -3,6 +3,8 @@ package org.briarproject.privategroup.invitation; import org.briarproject.api.FormatException; import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ProtocolStateException; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; import org.briarproject.api.db.Transaction; @@ -179,6 +181,7 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> { } catch (FormatException e) { throw new DbException(e); // Invalid group metadata } + // The relationship is already marked visible to the group // Move to the BOTH_JOINED state return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(), sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(), @@ -228,6 +231,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> { } catch (FormatException e) { throw new DbException(e); // Invalid group metadata } + try { + // Mark the relationship visible to the group, revealed by contact + relationshipRevealed(txn, s, true); + } catch (FormatException e) { + throw new DbException(e); // Invalid group metadata + } // Move to the BOTH_JOINED state return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(), sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(), @@ -254,6 +263,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> { Message sent = sendJoinMessage(txn, s, false); // Start syncing the private group with the contact syncPrivateGroupWithContact(txn, s, true); + // Mark the relationship visible to the group, revealed by contact + relationshipRevealed(txn, s, true); // Move to the BOTH_JOINED state return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(), sent.getId(), m.getId(), sent.getTimestamp(), BOTH_JOINED); @@ -266,6 +277,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> { return abort(txn, s); // Start syncing the private group with the contact syncPrivateGroupWithContact(txn, s, true); + // Mark the relationship visible to the group, revealed by us + relationshipRevealed(txn, s, false); // Move to the BOTH_JOINED state return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(), s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(), @@ -321,4 +334,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> { sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(), ERROR); } + + private void relationshipRevealed(Transaction txn, PeerSession s, + boolean byContact) throws DbException, FormatException { + ContactId contactId = getContactId(txn, s.getContactGroupId()); + Contact contact = db.getContact(txn, contactId); + privateGroupManager.relationshipRevealed(txn, s.getPrivateGroupId(), + contact.getAuthor().getId(), byContact); + } }