diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
index 2feaa4e95..a7a72e1fb 100644
--- a/.idea/codeStyleSettings.xml
+++ b/.idea/codeStyleSettings.xml
@@ -31,6 +31,8 @@
+
+
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 489e51a59..59475c974 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 c2bdf6cf4..5ff1193f9 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 ae11b48b8..d4a51798f 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 handleAction(Transaction txn,
LocalAction type, S session, ProtocolEngine 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 6d3c721f3..84aa82ced 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 {
} 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 {
} 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 {
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 {
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 {
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);
+ }
}