mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Merge branch '756-avoid-lost-messages' into 'master'
Use new group visibility state to avoid lost messages Depends on !410. Closes #756. See merge request !411
This commit is contained in:
@@ -27,8 +27,8 @@ import static org.briarproject.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.DISSOLVED;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.ERROR;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.INVITED;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.INVITEE_JOINED;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.INVITEE_LEFT;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.JOINED;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.LEFT;
|
||||
import static org.briarproject.privategroup.invitation.CreatorState.START;
|
||||
|
||||
@Immutable
|
||||
@@ -55,8 +55,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
case START:
|
||||
return onLocalInvite(txn, s, message, timestamp, signature);
|
||||
case INVITED:
|
||||
case INVITEE_JOINED:
|
||||
case INVITEE_LEFT:
|
||||
case JOINED:
|
||||
case LEFT:
|
||||
case DISSOLVED:
|
||||
case ERROR:
|
||||
throw new ProtocolStateException(); // Invalid in these states
|
||||
@@ -80,8 +80,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
case ERROR:
|
||||
return s; // Ignored in these states
|
||||
case INVITED:
|
||||
case INVITEE_JOINED:
|
||||
case INVITEE_LEFT:
|
||||
case JOINED:
|
||||
case LEFT:
|
||||
return onLocalLeave(txn, s);
|
||||
default:
|
||||
throw new AssertionError();
|
||||
@@ -105,8 +105,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
JoinMessage m) throws DbException, FormatException {
|
||||
switch (s.getState()) {
|
||||
case START:
|
||||
case INVITEE_JOINED:
|
||||
case INVITEE_LEFT:
|
||||
case JOINED:
|
||||
case LEFT:
|
||||
return abort(txn, s); // Invalid in these states
|
||||
case INVITED:
|
||||
return onRemoteAccept(txn, s, m);
|
||||
@@ -123,11 +123,11 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
LeaveMessage m) throws DbException, FormatException {
|
||||
switch (s.getState()) {
|
||||
case START:
|
||||
case INVITEE_LEFT:
|
||||
case LEFT:
|
||||
return abort(txn, s); // Invalid in these states
|
||||
case INVITED:
|
||||
return onRemoteDecline(txn, s, m);
|
||||
case INVITEE_JOINED:
|
||||
case JOINED:
|
||||
return onRemoteLeave(txn, s, m);
|
||||
case DISSOLVED:
|
||||
case ERROR:
|
||||
@@ -180,6 +180,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
// The dependency, if any, must be the last remote message
|
||||
if (!isValidDependency(s, m.getPreviousMessageId()))
|
||||
return abort(txn, s);
|
||||
// Send a JOIN message
|
||||
Message sent = sendJoinMessage(txn, s, false);
|
||||
// Mark the response visible in the UI
|
||||
markMessageVisibleInUi(txn, m.getId(), true);
|
||||
// Track the message
|
||||
@@ -191,10 +193,10 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
ContactId contactId = getContactId(txn, m.getContactGroupId());
|
||||
txn.attach(new GroupInvitationResponseReceivedEvent(contactId,
|
||||
createInvitationResponse(m, contactId, true)));
|
||||
// Move to the INVITEE_JOINED state
|
||||
// Move to the JOINED state
|
||||
return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
||||
s.getInviteTimestamp(), INVITEE_JOINED);
|
||||
sent.getId(), m.getId(), sent.getTimestamp(),
|
||||
s.getInviteTimestamp(), JOINED);
|
||||
}
|
||||
|
||||
private CreatorSession onRemoteDecline(Transaction txn, CreatorSession s,
|
||||
@@ -228,10 +230,10 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
return abort(txn, s);
|
||||
// Make the private group invisible to the contact
|
||||
setPrivateGroupVisibility(txn, s, INVISIBLE);
|
||||
// Move to the INVITEE_LEFT state
|
||||
// Move to the LEFT state
|
||||
return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
||||
s.getInviteTimestamp(), INVITEE_LEFT);
|
||||
s.getInviteTimestamp(), LEFT);
|
||||
}
|
||||
|
||||
private CreatorSession abort(Transaction txn, CreatorSession s)
|
||||
|
||||
@@ -4,8 +4,7 @@ import org.briarproject.api.FormatException;
|
||||
|
||||
enum CreatorState implements State {
|
||||
|
||||
START(0), INVITED(1), INVITEE_JOINED(2), INVITEE_LEFT(3), DISSOLVED(4),
|
||||
ERROR(5);
|
||||
START(0), INVITED(1), JOINED(2), LEFT(3), DISSOLVED(4), ERROR(5);
|
||||
|
||||
private final int value;
|
||||
|
||||
|
||||
@@ -27,11 +27,13 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.api.sync.Group.Visibility.VISIBLE;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.ACCEPTED;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.DISSOLVED;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.ERROR;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.INVITED;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.INVITEE_JOINED;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.INVITEE_LEFT;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.JOINED;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.LEFT;
|
||||
import static org.briarproject.privategroup.invitation.InviteeState.START;
|
||||
|
||||
@Immutable
|
||||
@@ -62,8 +64,9 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
throws DbException {
|
||||
switch (s.getState()) {
|
||||
case START:
|
||||
case INVITEE_JOINED:
|
||||
case INVITEE_LEFT:
|
||||
case ACCEPTED:
|
||||
case JOINED:
|
||||
case LEFT:
|
||||
case DISSOLVED:
|
||||
case ERROR:
|
||||
throw new ProtocolStateException(); // Invalid in these states
|
||||
@@ -79,13 +82,14 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
throws DbException {
|
||||
switch (s.getState()) {
|
||||
case START:
|
||||
case INVITEE_LEFT:
|
||||
case LEFT:
|
||||
case DISSOLVED:
|
||||
case ERROR:
|
||||
return s; // Ignored in these states
|
||||
case INVITED:
|
||||
return onLocalDecline(txn, s);
|
||||
case INVITEE_JOINED:
|
||||
case ACCEPTED:
|
||||
case JOINED:
|
||||
return onLocalLeave(txn, s);
|
||||
default:
|
||||
throw new AssertionError();
|
||||
@@ -105,8 +109,9 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
case START:
|
||||
return onRemoteInvite(txn, s, m);
|
||||
case INVITED:
|
||||
case INVITEE_JOINED:
|
||||
case INVITEE_LEFT:
|
||||
case ACCEPTED:
|
||||
case JOINED:
|
||||
case LEFT:
|
||||
case DISSOLVED:
|
||||
return abort(txn, s); // Invalid in these states
|
||||
case ERROR:
|
||||
@@ -119,7 +124,20 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
@Override
|
||||
public InviteeSession onJoinMessage(Transaction txn, InviteeSession s,
|
||||
JoinMessage m) throws DbException, FormatException {
|
||||
return abort(txn, s); // Invalid in this role
|
||||
switch (s.getState()) {
|
||||
case START:
|
||||
case INVITED:
|
||||
case JOINED:
|
||||
case LEFT:
|
||||
case DISSOLVED:
|
||||
return abort(txn, s); // Invalid in these states
|
||||
case ACCEPTED:
|
||||
return onRemoteJoin(txn, s, m);
|
||||
case ERROR:
|
||||
return s; // Ignored in this state
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,8 +148,9 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
case DISSOLVED:
|
||||
return abort(txn, s); // Invalid in these states
|
||||
case INVITED:
|
||||
case INVITEE_JOINED:
|
||||
case INVITEE_LEFT:
|
||||
case ACCEPTED:
|
||||
case JOINED:
|
||||
case LEFT:
|
||||
return onRemoteLeave(txn, s, m);
|
||||
case ERROR:
|
||||
return s; // Ignored in this state
|
||||
@@ -159,15 +178,15 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
try {
|
||||
// Subscribe to the private group
|
||||
subscribeToPrivateGroup(txn, inviteId);
|
||||
// Share the private group with the contact
|
||||
setPrivateGroupVisibility(txn, s, SHARED);
|
||||
// Make the private group visible to the contact
|
||||
setPrivateGroupVisibility(txn, s, VISIBLE);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
// Move to the INVITEE_JOINED state
|
||||
// Move to the ACCEPTED state
|
||||
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
||||
s.getInviteTimestamp(), INVITEE_JOINED);
|
||||
s.getInviteTimestamp(), ACCEPTED);
|
||||
}
|
||||
|
||||
private InviteeSession onLocalDecline(Transaction txn, InviteeSession s)
|
||||
@@ -190,10 +209,10 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
throws DbException {
|
||||
// Send a LEAVE message
|
||||
Message sent = sendLeaveMessage(txn, s, false);
|
||||
// Move to the INVITEE_LEFT state
|
||||
// Move to the LEFT state
|
||||
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
||||
s.getInviteTimestamp(), INVITEE_LEFT);
|
||||
s.getInviteTimestamp(), LEFT);
|
||||
}
|
||||
|
||||
private InviteeSession onRemoteInvite(Transaction txn, InviteeSession s,
|
||||
@@ -222,6 +241,25 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
m.getTimestamp(), INVITED);
|
||||
}
|
||||
|
||||
private InviteeSession onRemoteJoin(Transaction txn, InviteeSession s,
|
||||
JoinMessage m) throws DbException, FormatException {
|
||||
// The timestamp must be higher than the last invite message, if any
|
||||
if (m.getTimestamp() <= s.getInviteTimestamp()) return abort(txn, s);
|
||||
// The dependency, if any, must be the last remote message
|
||||
if (!isValidDependency(s, m.getPreviousMessageId()))
|
||||
return abort(txn, s);
|
||||
try {
|
||||
// Share the private group with the contact
|
||||
setPrivateGroupVisibility(txn, s, SHARED);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
// Move to the JOINED state
|
||||
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
||||
s.getInviteTimestamp(), JOINED);
|
||||
}
|
||||
|
||||
private InviteeSession onRemoteLeave(Transaction txn, InviteeSession s,
|
||||
LeaveMessage m) throws DbException, FormatException {
|
||||
// The timestamp must be higher than the last invite message, if any
|
||||
|
||||
@@ -4,8 +4,8 @@ import org.briarproject.api.FormatException;
|
||||
|
||||
enum InviteeState implements State {
|
||||
|
||||
START(0), INVITED(1), INVITEE_JOINED(2), INVITEE_LEFT(3), DISSOLVED(4),
|
||||
ERROR(5);
|
||||
START(0), INVITED(1), ACCEPTED(2), JOINED(3), LEFT(4), DISSOLVED(5),
|
||||
ERROR(6);
|
||||
|
||||
private final int value;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.api.sync.Group.Visibility.VISIBLE;
|
||||
import static org.briarproject.privategroup.invitation.PeerState.AWAIT_MEMBER;
|
||||
import static org.briarproject.privategroup.invitation.PeerState.BOTH_JOINED;
|
||||
import static org.briarproject.privategroup.invitation.PeerState.ERROR;
|
||||
@@ -169,6 +170,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
||||
PeerSession s) throws DbException {
|
||||
// Send a JOIN message
|
||||
Message sent = sendJoinMessage(txn, s, false);
|
||||
try {
|
||||
// Make the private group visible to the contact
|
||||
setPrivateGroupVisibility(txn, s, VISIBLE);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
// Move to the LOCAL_JOINED state
|
||||
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
||||
@@ -212,6 +219,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
||||
PeerSession s) throws DbException {
|
||||
// Send a LEAVE message
|
||||
Message sent = sendLeaveMessage(txn, s, false);
|
||||
try {
|
||||
// Make the private group invisible to the contact
|
||||
setPrivateGroupVisibility(txn, s, INVISIBLE);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
// Move to the NEITHER_JOINED state
|
||||
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
||||
@@ -316,8 +329,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
||||
// The dependency, if any, must be the last remote message
|
||||
if (!isValidDependency(s, m.getPreviousMessageId()))
|
||||
return abort(txn, s);
|
||||
// Make the private group invisible to the contact
|
||||
setPrivateGroupVisibility(txn, s, INVISIBLE);
|
||||
// Unshare the private group with the contact
|
||||
setPrivateGroupVisibility(txn, s, VISIBLE);
|
||||
// Move to the LOCAL_JOINED state
|
||||
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
||||
|
||||
Reference in New Issue
Block a user