Remember when declines were automatic due to deletion

so they can be shown differently for sender
This commit is contained in:
Torsten Grote
2021-03-01 17:00:16 -03:00
parent a8cff454ec
commit 4d3482e40e
25 changed files with 218 additions and 124 deletions

View File

@@ -15,7 +15,7 @@ public class BlogInvitationResponse extends InvitationResponse {
SessionId sessionId, boolean accept, GroupId shareableId, SessionId sessionId, boolean accept, GroupId shareableId,
long autoDeleteTimer) { long autoDeleteTimer) {
super(id, groupId, time, local, read, sent, seen, sessionId, super(id, groupId, time, local, read, sent, seen, sessionId,
accept, shareableId, autoDeleteTimer); accept, shareableId, autoDeleteTimer, false);
} }
@Override @Override

View File

@@ -12,14 +12,16 @@ import javax.annotation.concurrent.Immutable;
public abstract class ConversationResponse extends ConversationMessageHeader { public abstract class ConversationResponse extends ConversationMessageHeader {
private final SessionId sessionId; private final SessionId sessionId;
private final boolean accepted; private final boolean accepted, isAutoDecline;
public ConversationResponse(MessageId id, GroupId groupId, long time, public ConversationResponse(MessageId id, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, boolean accepted, long autoDeleteTimer) { SessionId sessionId, boolean accepted, long autoDeleteTimer,
boolean isAutoDecline) {
super(id, groupId, time, local, read, sent, seen, autoDeleteTimer); super(id, groupId, time, local, read, sent, seen, autoDeleteTimer);
this.sessionId = sessionId; this.sessionId = sessionId;
this.accepted = accepted; this.accepted = accepted;
this.isAutoDecline = isAutoDecline;
} }
public SessionId getSessionId() { public SessionId getSessionId() {
@@ -30,4 +32,7 @@ public abstract class ConversationResponse extends ConversationMessageHeader {
return accepted; return accepted;
} }
public boolean isAutoDecline() {
return isAutoDecline;
}
} }

View File

@@ -18,7 +18,7 @@ public class ForumInvitationResponse extends InvitationResponse {
SessionId sessionId, boolean accept, GroupId shareableId, SessionId sessionId, boolean accept, GroupId shareableId,
long autoDeleteTimer) { long autoDeleteTimer) {
super(id, groupId, time, local, read, sent, seen, sessionId, super(id, groupId, time, local, read, sent, seen, sessionId,
accept, shareableId, autoDeleteTimer); accept, shareableId, autoDeleteTimer, false);
} }
@Override @Override

View File

@@ -1,13 +1,13 @@
package org.briarproject.briar.api.introduction; package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationMessageVisitor; import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
import org.briarproject.briar.api.conversation.ConversationResponse; import org.briarproject.briar.api.conversation.ConversationResponse;
import org.briarproject.briar.api.identity.AuthorInfo;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -28,7 +28,7 @@ public class IntroductionResponse extends ConversationResponse {
AuthorInfo introducedAuthorInfo, Role role, boolean canSucceed, AuthorInfo introducedAuthorInfo, Role role, boolean canSucceed,
long autoDeleteTimer) { long autoDeleteTimer) {
super(messageId, groupId, time, local, read, sent, seen, sessionId, super(messageId, groupId, time, local, read, sent, seen, sessionId,
accepted, autoDeleteTimer); accepted, autoDeleteTimer, false);
this.introducedAuthor = author; this.introducedAuthor = author;
this.introducedAuthorInfo = introducedAuthorInfo; this.introducedAuthorInfo = introducedAuthorInfo;
this.ourRole = role; this.ourRole = role;

View File

@@ -16,9 +16,9 @@ public class GroupInvitationResponse extends InvitationResponse {
public GroupInvitationResponse(MessageId id, GroupId groupId, long time, public GroupInvitationResponse(MessageId id, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, boolean accept, GroupId shareableId, SessionId sessionId, boolean accept, GroupId shareableId,
long autoDeleteTimer) { long autoDeleteTimer, boolean isAutoDecline) {
super(id, groupId, time, local, read, sent, seen, sessionId, super(id, groupId, time, local, read, sent, seen, sessionId,
accept, shareableId, autoDeleteTimer); accept, shareableId, autoDeleteTimer, isAutoDecline);
} }
@Override @Override

View File

@@ -12,9 +12,9 @@ public abstract class InvitationResponse extends ConversationResponse {
public InvitationResponse(MessageId id, GroupId groupId, long time, public InvitationResponse(MessageId id, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, boolean accepted, GroupId shareableId, SessionId sessionId, boolean accepted, GroupId shareableId,
long autoDeleteTimer) { long autoDeleteTimer, boolean isAutoDecline) {
super(id, groupId, time, local, read, sent, seen, sessionId, accepted, super(id, groupId, time, local, read, sent, seen, sessionId, accepted,
autoDeleteTimer); autoDeleteTimer, isAutoDecline);
this.shareableId = shareableId; this.shareableId = shareableId;
} }

View File

@@ -180,15 +180,20 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
return m; return m;
} }
Message sendLeaveMessage(Transaction txn, S s, boolean visibleInUi) Message sendLeaveMessage(Transaction txn, S s) throws DbException {
throws DbException { return sendLeaveMessage(txn, s, false, false);
}
Message sendLeaveMessage(Transaction txn, S s, boolean visibleInUi,
boolean isAutoDecline) throws DbException {
if (!visibleInUi && isAutoDecline) throw new IllegalArgumentException();
Message m; Message m;
long localTimestamp = visibleInUi long localTimestamp = visibleInUi
? getTimestampForVisibleMessage(txn, s) ? getTimestampForVisibleMessage(txn, s)
: getTimestampForInvisibleMessage(s); : getTimestampForInvisibleMessage(s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
// Set auto-delete timer if manually accepting an invitation // Set auto-delete timer if declining an invitation
long timer = NO_AUTO_DELETE_TIMER; long timer = NO_AUTO_DELETE_TIMER;
if (visibleInUi) { if (visibleInUi) {
timer = autoDeleteManager.getAutoDeleteTimer(txn, c, timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
@@ -198,8 +203,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
s.getPrivateGroupId(), localTimestamp, s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);
sendMessage(txn, m, LEAVE, s.getPrivateGroupId(), visibleInUi, sendMessage(txn, m, LEAVE, s.getPrivateGroupId(), visibleInUi,
timer); timer, isAutoDecline);
// Set the auto-delete timer duration on the message // Set the auto-delete timer duration on the local message
if (timer != NO_AUTO_DELETE_TIMER) { if (timer != NO_AUTO_DELETE_TIMER) {
db.setCleanupTimerDuration(txn, m.getId(), timer); db.setCleanupTimerDuration(txn, m.getId(), timer);
} }
@@ -321,9 +326,17 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
private void sendMessage(Transaction txn, Message m, MessageType type, private void sendMessage(Transaction txn, Message m, MessageType type,
GroupId privateGroupId, boolean visibleInConversation, GroupId privateGroupId, boolean visibleInConversation,
long autoDeleteTimer) throws DbException { long autoDeleteTimer) throws DbException {
sendMessage(txn, m, type, privateGroupId, visibleInConversation,
autoDeleteTimer, false);
}
private void sendMessage(Transaction txn, Message m, MessageType type,
GroupId privateGroupId, boolean visibleInConversation,
long autoDeleteTimer, boolean isAutoDecline) throws DbException {
BdfDictionary meta = messageEncoder.encodeMetadata(type, BdfDictionary meta = messageEncoder.encodeMetadata(type,
privateGroupId, m.getTimestamp(), true, true, privateGroupId, m.getTimestamp(), true, true,
visibleInConversation, false, false, autoDeleteTimer); visibleInConversation, false, false, autoDeleteTimer,
isAutoDecline);
try { try {
clientHelper.addLocalMessage(txn, m, meta, true, false); clientHelper.addLocalMessage(txn, m, meta, true, false);
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -84,8 +84,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
} }
@Override @Override
public CreatorSession onLeaveAction(Transaction txn, CreatorSession s) public CreatorSession onLeaveAction(Transaction txn, CreatorSession s,
throws DbException { boolean isAutoDecline) throws DbException {
switch (s.getState()) { switch (s.getState()) {
case START: case START:
case DISSOLVED: case DISSOLVED:
@@ -180,7 +180,7 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
throw new DbException(e); // Invalid group metadata throw new DbException(e); // Invalid group metadata
} }
// Send a LEAVE message // Send a LEAVE message
Message sent = sendLeaveMessage(txn, s, false); Message sent = sendLeaveMessage(txn, s);
// Move to the DISSOLVED state // Move to the DISSOLVED state
return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(), return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(), sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
@@ -276,6 +276,6 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes()); SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
return new GroupInvitationResponse(m.getId(), m.getContactGroupId(), return new GroupInvitationResponse(m.getId(), m.getContactGroupId(),
m.getTimestamp(), false, false, false, false, sessionId, m.getTimestamp(), false, false, false, false, sessionId,
accept, m.getPrivateGroupId(), m.getAutoDeleteTimer()); accept, m.getPrivateGroupId(), m.getAutoDeleteTimer(), false);
} }
} }

View File

@@ -11,6 +11,7 @@ interface GroupInvitationConstants {
String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer"; String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer";
String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted"; String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted";
String MSG_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer"; String MSG_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer";
String MSG_KEY_IS_AUTO_DECLINE = "isAutoDecline";
// Session keys // Session keys
String SESSION_KEY_IS_SESSION = "isSession"; String SESSION_KEY_IS_SESSION = "isSession";

View File

@@ -310,11 +310,12 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
public void respondToInvitation(ContactId c, SessionId sessionId, public void respondToInvitation(ContactId c, SessionId sessionId,
boolean accept) throws DbException { boolean accept) throws DbException {
db.transaction(false, db.transaction(false,
txn -> respondToInvitation(txn, c, sessionId, accept)); txn -> respondToInvitation(txn, c, sessionId, accept, false));
} }
private void respondToInvitation(Transaction txn, ContactId c, private void respondToInvitation(Transaction txn, ContactId c,
SessionId sessionId, boolean accept) throws DbException { SessionId sessionId, boolean accept, boolean isAutoDecline)
throws DbException {
try { try {
// Look up the session // Look up the session
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
@@ -326,7 +327,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
.parseInviteeSession(contactGroupId, ss.bdfSession); .parseInviteeSession(contactGroupId, ss.bdfSession);
// Handle the join or leave action // Handle the join or leave action
if (accept) session = inviteeEngine.onJoinAction(txn, session); if (accept) session = inviteeEngine.onJoinAction(txn, session);
else session = inviteeEngine.onLeaveAction(txn, session); else session =
inviteeEngine.onLeaveAction(txn, session, isAutoDecline);
// Store the updated session // Store the updated session
storeSession(txn, ss.storageId, session); storeSession(txn, ss.storageId, session);
} catch (FormatException e) { } catch (FormatException e) {
@@ -366,7 +368,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} else if (type == LocalAction.JOIN) { } else if (type == LocalAction.JOIN) {
return engine.onJoinAction(txn, session); return engine.onJoinAction(txn, session);
} else if (type == LocalAction.LEAVE) { } else if (type == LocalAction.LEAVE) {
return engine.onLeaveAction(txn, session); return engine.onLeaveAction(txn, session, false);
} else if (type == LocalAction.MEMBER_ADDED) { } else if (type == LocalAction.MEMBER_ADDED) {
return engine.onMemberAddedAction(txn, session); return engine.onMemberAddedAction(txn, session);
} else { } else {
@@ -435,7 +437,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
return new GroupInvitationResponse(m, contactGroupId, return new GroupInvitationResponse(m, contactGroupId,
meta.getTimestamp(), meta.isLocal(), meta.isRead(), meta.getTimestamp(), meta.isLocal(), meta.isRead(),
status.isSent(), status.isSeen(), sessionId, accept, status.isSent(), status.isSeen(), sessionId, accept,
meta.getPrivateGroupId(), meta.getAutoDeleteTimer()); meta.getPrivateGroupId(), meta.getAutoDeleteTimer(),
meta.isAutoDecline());
} }
@Override @Override
@@ -813,7 +816,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
// decline invitee sessions waiting for a response before // decline invitee sessions waiting for a response before
if (session.state instanceof InviteeState && if (session.state instanceof InviteeState &&
session.state.isAwaitingResponse()) { session.state.isAwaitingResponse()) {
respondToInvitation(txn, c, entry.getKey(), false); respondToInvitation(txn, c, entry.getKey(), false, true);
} }
for (MessageId m : session.messages) { for (MessageId m : session.messages) {
db.deleteMessage(txn, m); db.deleteMessage(txn, m);

View File

@@ -110,8 +110,7 @@ class GroupInvitationValidator extends BdfMessageValidator {
} }
// Create the metadata // Create the metadata
BdfDictionary meta = messageEncoder.encodeMetadata(INVITE, BdfDictionary meta = messageEncoder.encodeMetadata(INVITE,
privateGroup.getId(), m.getTimestamp(), false, false, false, privateGroup.getId(), m.getTimestamp(), timer);
false, false, timer);
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }
@@ -132,8 +131,7 @@ class GroupInvitationValidator extends BdfMessageValidator {
} }
BdfDictionary meta = messageEncoder.encodeMetadata(JOIN, BdfDictionary meta = messageEncoder.encodeMetadata(JOIN,
new GroupId(privateGroupId), m.getTimestamp(), false, false, new GroupId(privateGroupId), m.getTimestamp(), timer);
false, false, false, timer);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -160,8 +158,7 @@ class GroupInvitationValidator extends BdfMessageValidator {
} }
BdfDictionary meta = messageEncoder.encodeMetadata(LEAVE, BdfDictionary meta = messageEncoder.encodeMetadata(LEAVE,
new GroupId(privateGroupId), m.getTimestamp(), false, false, new GroupId(privateGroupId), m.getTimestamp(), timer);
false, false, false, timer);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -177,8 +174,8 @@ class GroupInvitationValidator extends BdfMessageValidator {
byte[] privateGroupId = body.getRaw(1); byte[] privateGroupId = body.getRaw(1);
checkLength(privateGroupId, UniqueId.LENGTH); checkLength(privateGroupId, UniqueId.LENGTH);
BdfDictionary meta = messageEncoder.encodeMetadata(ABORT, BdfDictionary meta = messageEncoder.encodeMetadata(ABORT,
new GroupId(privateGroupId), m.getTimestamp(), false, false, new GroupId(privateGroupId), m.getTimestamp(),
false, false, false, NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }
} }

View File

@@ -89,8 +89,8 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
} }
@Override @Override
public InviteeSession onLeaveAction(Transaction txn, InviteeSession s) public InviteeSession onLeaveAction(Transaction txn, InviteeSession s,
throws DbException { boolean isAutoDecline) throws DbException {
switch (s.getState()) { switch (s.getState()) {
case START: case START:
case LEFT: case LEFT:
@@ -98,7 +98,7 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
case ERROR: case ERROR:
return s; // Ignored in these states return s; // Ignored in these states
case INVITED: case INVITED:
return onLocalDecline(txn, s); return onLocalDecline(txn, s, isAutoDecline);
case ACCEPTED: case ACCEPTED:
case JOINED: case JOINED:
return onLocalLeave(txn, s); return onLocalLeave(txn, s);
@@ -203,14 +203,14 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
s.getInviteTimestamp(), ACCEPTED); s.getInviteTimestamp(), ACCEPTED);
} }
private InviteeSession onLocalDecline(Transaction txn, InviteeSession s) private InviteeSession onLocalDecline(Transaction txn, InviteeSession s,
throws DbException { boolean isAutoDecline) throws DbException {
// Mark the invite message unavailable to answer // Mark the invite message unavailable to answer
MessageId inviteId = s.getLastRemoteMessageId(); MessageId inviteId = s.getLastRemoteMessageId();
if (inviteId == null) throw new IllegalStateException(); if (inviteId == null) throw new IllegalStateException();
markMessageAvailableToAnswer(txn, inviteId, false); markMessageAvailableToAnswer(txn, inviteId, false);
// Send a LEAVE message // Send a LEAVE message
Message sent = sendLeaveMessage(txn, s, true); Message sent = sendLeaveMessage(txn, s, true, isAutoDecline);
// Track the message // Track the message
messageTracker.trackOutgoingMessage(txn, sent); messageTracker.trackOutgoingMessage(txn, sent);
// Move to the START state // Move to the START state
@@ -222,7 +222,7 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
private InviteeSession onLocalLeave(Transaction txn, InviteeSession s) private InviteeSession onLocalLeave(Transaction txn, InviteeSession s)
throws DbException { throws DbException {
// Send a LEAVE message // Send a LEAVE message
Message sent = sendLeaveMessage(txn, s, false); Message sent = sendLeaveMessage(txn, s);
try { try {
// Make the private group invisible to the contact // Make the private group invisible to the contact
setPrivateGroupVisibility(txn, s, INVISIBLE); setPrivateGroupVisibility(txn, s, INVISIBLE);

View File

@@ -14,7 +14,11 @@ interface MessageEncoder {
BdfDictionary encodeMetadata(MessageType type, GroupId privateGroupId, BdfDictionary encodeMetadata(MessageType type, GroupId privateGroupId,
long timestamp, boolean local, boolean read, boolean visible, long timestamp, boolean local, boolean read, boolean visible,
boolean available, boolean accepted, long autoDeleteTimer); boolean available, boolean accepted, long autoDeleteTimer,
boolean isAutoDecline);
BdfDictionary encodeMetadata(MessageType type, GroupId privateGroupId,
long timestamp, long autoDeleteTimer);
void setVisibleInUi(BdfDictionary meta, boolean visible); void setVisibleInUi(BdfDictionary meta, boolean visible);

View File

@@ -20,6 +20,7 @@ import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AUTO_DELETE_TIMER; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_INVITATION_ACCEPTED; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_IS_AUTO_DECLINE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_PRIVATE_GROUP_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_PRIVATE_GROUP_ID;
@@ -48,7 +49,7 @@ class MessageEncoderImpl implements MessageEncoder {
public BdfDictionary encodeMetadata(MessageType type, public BdfDictionary encodeMetadata(MessageType type,
GroupId privateGroupId, long timestamp, boolean local, boolean read, GroupId privateGroupId, long timestamp, boolean local, boolean read,
boolean visible, boolean available, boolean accepted, boolean visible, boolean available, boolean accepted,
long autoDeleteTimer) { long autoDeleteTimer, boolean isAutoDecline) {
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue()); meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue());
meta.put(MSG_KEY_PRIVATE_GROUP_ID, privateGroupId); meta.put(MSG_KEY_PRIVATE_GROUP_ID, privateGroupId);
@@ -61,9 +62,19 @@ class MessageEncoderImpl implements MessageEncoder {
if (autoDeleteTimer != NO_AUTO_DELETE_TIMER) { if (autoDeleteTimer != NO_AUTO_DELETE_TIMER) {
meta.put(MSG_KEY_AUTO_DELETE_TIMER, autoDeleteTimer); meta.put(MSG_KEY_AUTO_DELETE_TIMER, autoDeleteTimer);
} }
if (isAutoDecline) {
meta.put(MSG_KEY_IS_AUTO_DECLINE, isAutoDecline);
}
return meta; return meta;
} }
@Override
public BdfDictionary encodeMetadata(MessageType type,
GroupId privateGroupId, long timestamp, long autoDeleteTimer) {
return encodeMetadata(type, privateGroupId, timestamp, false, false,
false, false, false, autoDeleteTimer, false);
}
@Override @Override
public void setVisibleInUi(BdfDictionary meta, boolean visible) { public void setVisibleInUi(BdfDictionary meta, boolean visible) {
meta.put(MSG_KEY_VISIBLE_IN_UI, visible); meta.put(MSG_KEY_VISIBLE_IN_UI, visible);

View File

@@ -13,10 +13,12 @@ class MessageMetadata {
private final GroupId privateGroupId; private final GroupId privateGroupId;
private final long timestamp, autoDeleteTimer; private final long timestamp, autoDeleteTimer;
private final boolean local, read, visible, available, accepted; private final boolean local, read, visible, available, accepted;
private final boolean isAutoDecline;
MessageMetadata(MessageType type, GroupId privateGroupId, MessageMetadata(MessageType type, GroupId privateGroupId,
long timestamp, boolean local, boolean read, boolean visible, long timestamp, boolean local, boolean read, boolean visible,
boolean available, boolean accepted, long autoDeleteTimer) { boolean available, boolean accepted, long autoDeleteTimer,
boolean isAutoDecline) {
this.privateGroupId = privateGroupId; this.privateGroupId = privateGroupId;
this.type = type; this.type = type;
this.timestamp = timestamp; this.timestamp = timestamp;
@@ -26,6 +28,7 @@ class MessageMetadata {
this.available = available; this.available = available;
this.accepted = accepted; this.accepted = accepted;
this.autoDeleteTimer = autoDeleteTimer; this.autoDeleteTimer = autoDeleteTimer;
this.isAutoDecline = isAutoDecline;
} }
MessageType getMessageType() { MessageType getMessageType() {
@@ -58,7 +61,7 @@ class MessageMetadata {
/** /**
* Returns true if the invitation was accepted. * Returns true if the invitation was accepted.
* * <p>
* Only applies to messages of type INVITE. * Only applies to messages of type INVITE.
*/ */
public boolean wasAccepted() { public boolean wasAccepted() {
@@ -68,4 +71,8 @@ class MessageMetadata {
public long getAutoDeleteTimer() { public long getAutoDeleteTimer() {
return autoDeleteTimer; return autoDeleteTimer;
} }
public boolean isAutoDecline() {
return isAutoDecline;
}
} }

View File

@@ -23,6 +23,7 @@ import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AUTO_DELETE_TIMER; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_INVITATION_ACCEPTED; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_IS_AUTO_DECLINE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_PRIVATE_GROUP_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_PRIVATE_GROUP_ID;
@@ -82,8 +83,9 @@ class MessageParserImpl implements MessageParser {
boolean accepted = meta.getBoolean(MSG_KEY_INVITATION_ACCEPTED, false); boolean accepted = meta.getBoolean(MSG_KEY_INVITATION_ACCEPTED, false);
long timer = meta.getLong(MSG_KEY_AUTO_DELETE_TIMER, long timer = meta.getLong(MSG_KEY_AUTO_DELETE_TIMER,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
boolean isAutoDecline = meta.getBoolean(MSG_KEY_IS_AUTO_DECLINE, false);
return new MessageMetadata(type, privateGroupId, timestamp, local, read, return new MessageMetadata(type, privateGroupId, timestamp, local, read,
visible, available, accepted, timer); visible, available, accepted, timer, isAutoDecline);
} }
@Override @Override

View File

@@ -85,8 +85,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
} }
@Override @Override
public PeerSession onLeaveAction(Transaction txn, PeerSession s) public PeerSession onLeaveAction(Transaction txn, PeerSession s,
throws DbException { boolean isAutoDecline) throws DbException {
switch (s.getState()) { switch (s.getState()) {
case START: case START:
case AWAIT_MEMBER: case AWAIT_MEMBER:
@@ -213,7 +213,7 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
private PeerSession onLocalLeaveFromBothJoined(Transaction txn, private PeerSession onLocalLeaveFromBothJoined(Transaction txn,
PeerSession s) throws DbException { PeerSession s) throws DbException {
// Send a LEAVE message // Send a LEAVE message
Message sent = sendLeaveMessage(txn, s, false); Message sent = sendLeaveMessage(txn, s);
try { try {
// Make the private group invisible to the contact // Make the private group invisible to the contact
setPrivateGroupVisibility(txn, s, INVISIBLE); setPrivateGroupVisibility(txn, s, INVISIBLE);
@@ -229,7 +229,7 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
private PeerSession onLocalLeaveFromLocalJoined(Transaction txn, private PeerSession onLocalLeaveFromLocalJoined(Transaction txn,
PeerSession s) throws DbException { PeerSession s) throws DbException {
// Send a LEAVE message // Send a LEAVE message
Message sent = sendLeaveMessage(txn, s, false); Message sent = sendLeaveMessage(txn, s);
try { try {
// Make the private group invisible to the contact // Make the private group invisible to the contact
setPrivateGroupVisibility(txn, s, INVISIBLE); setPrivateGroupVisibility(txn, s, INVISIBLE);

View File

@@ -16,7 +16,14 @@ interface ProtocolEngine<S extends Session<?>> {
S onJoinAction(Transaction txn, S session) throws DbException; S onJoinAction(Transaction txn, S session) throws DbException;
S onLeaveAction(Transaction txn, S session) throws DbException; /**
* Leaves the group or declines an invitation.
*
* @param isAutoDecline true if automatically declined due to deletion
* and false if initiated by the user.
*/
S onLeaveAction(Transaction txn, S session, boolean isAutoDecline)
throws DbException;
S onMemberAddedAction(Transaction txn, S session) throws DbException; S onMemberAddedAction(Transaction txn, S session) throws DbException;

View File

@@ -194,7 +194,7 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageEncoder).encodeMetadata(type, privateGroupId, oneOf(messageEncoder).encodeMetadata(type, privateGroupId,
message.getTimestamp(), true, true, visible, false, false, message.getTimestamp(), true, true, visible, false, false,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER, false);
will(returnValue(meta)); will(returnValue(meta));
oneOf(clientHelper).addLocalMessage(txn, message, meta, true, oneOf(clientHelper).addLocalMessage(txn, message, meta, true,
false); false);

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient; import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
import org.briarproject.briar.api.privategroup.GroupMessage; import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -17,8 +16,6 @@ import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.bramble.api.cleanup.CleanupManager.BATCH_DELAY_MS; import static org.briarproject.bramble.api.cleanup.CleanupManager.BATCH_DELAY_MS;
@@ -28,6 +25,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest { public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
@@ -104,10 +102,9 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
assertEquals(1, getMessageHeaders(c1, contactId0From1).size()); assertEquals(1, getMessageHeaders(c1, contactId0From1).size());
// 1 marks the message as read - this starts 1's timer // 1 marks the message as read - this starts 1's timer
final MessageId messageId = final MessageId messageId0 =
getMessageHeaders(c1, contactId0From1).get(0).getId(); getMessageHeaders(c1, contactId0From1).get(0).getId();
markMessageRead(c1, contact0From1, messageId); markMessageRead(c1, contact0From1, messageId0);
waitForEvents(c1);
assertGroupCount(c1, contactId0From1, 1, 0); assertGroupCount(c1, contactId0From1, 1, 0);
// Before 1's timer elapses, 1 should still see the message // Before 1's timer elapses, 1 should still see the message
@@ -127,9 +124,10 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
assertGroupCount(c1, contactId0From1, 1, 0); assertGroupCount(c1, contactId0From1, 1, 0);
forEachHeader(c1, contactId0From1, 1, h -> { forEachHeader(c1, contactId0From1, 1, h -> {
// The only message is not the same as before, but declined response // The only message is not the same as before, but declined response
assertNotEquals(messageId, h.getId()); assertNotEquals(messageId0, h.getId());
assertTrue(h instanceof GroupInvitationResponse); assertTrue(h instanceof GroupInvitationResponse);
assertFalse(((GroupInvitationResponse) h).wasAccepted()); assertFalse(((GroupInvitationResponse) h).wasAccepted());
assertTrue(((GroupInvitationResponse) h).isAutoDecline());
// The auto-decline message should have the expected timer // The auto-decline message should have the expected timer
assertEquals(MIN_AUTO_DELETE_TIMER_MS, assertEquals(MIN_AUTO_DELETE_TIMER_MS,
h.getAutoDeleteTimer()); h.getAutoDeleteTimer());
@@ -162,10 +160,9 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
assertEquals(0, getMessageHeaders(c1, contactId0From1).size()); assertEquals(0, getMessageHeaders(c1, contactId0From1).size());
// 0 marks the message as read - this starts 0's timer // 0 marks the message as read - this starts 0's timer
MessageId messageId0 = MessageId messageId1 =
getMessageHeaders(c0, contactId1From0).get(0).getId(); getMessageHeaders(c0, contactId1From0).get(0).getId();
markMessageRead(c0, contact1From0, messageId0); markMessageRead(c0, contact1From0, messageId1);
waitForEvents(c0);
assertGroupCount(c0, contactId1From0, 1, 0); assertGroupCount(c0, contactId1From0, 1, 0);
// Before 0's timer elapses, 0 should still see the message // Before 0's timer elapses, 0 should still see the message
@@ -174,7 +171,7 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
assertEquals(1, getMessageHeaders(c0, contactId1From0).size()); assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
// When 0's timer has elapsed, the message should be deleted from 0's // When 0's timer has elapsed, the message should be deleted from 0's
// view of the conversation but 1 should still see the message // view of the conversation
c0.getTimeTravel().addCurrentTimeMillis(1); c0.getTimeTravel().addCurrentTimeMillis(1);
assertGroupCount(c0, contactId1From0, 0, 0); assertGroupCount(c0, contactId1From0, 0, 0);
assertEquals(0, getMessageHeaders(c0, contactId1From0).size()); assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
@@ -196,6 +193,7 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
sendInvitation(pg, contact1From0.getId(), null); sendInvitation(pg, contact1From0.getId(), null);
sync0To1(1, true); sync0To1(1, true);
ack1To0(1); ack1To0(1);
waitForEvents(c0);
// The message should have been added the views of the conversation // The message should have been added the views of the conversation
assertGroupCount(c0, contactId1From0, 1, 0); assertGroupCount(c0, contactId1From0, 1, 0);
@@ -239,25 +237,24 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
assertGroupCount(c0, contactId1From0, 1, 0); assertGroupCount(c0, contactId1From0, 1, 0);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size()); assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 2, 2); assertGroupCount(c1, contactId0From1, 2, 2);
List<ConversationMessageHeader> headers1 = assertEquals(2, getMessageHeaders(c1, contactId0From1).size());
getMessageHeaders(c1, contactId0From1);
assertEquals(2, headers1.size());
// 1 marks the message as read - this starts 1's timer // 1 marks all the message as read - this starts 1's timer for 2nd msg
ConversationMessageHeader header = headers1.get(1); // newer message forEachHeader(c1, contactId0From1, 2, h -> {
assertEquals(privateGroup, try {
((GroupInvitationRequest) header).getNameable()); markMessageRead(c1, contact0From1, h.getId());
MessageId messageId = header.getId(); } catch (Exception e) {
markMessageRead(c1, contact0From1, messageId); fail();
waitForEvents(c1); }
assertGroupCount(c1, contactId0From1, 2, 1); });
assertGroupCount(c1, contactId0From1, 2, 0);
// Before 1's timer elapses, 1 should still see the message // Before 1's timer elapses, 1 should still see the message
c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1); c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1); c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
assertGroupCount(c0, contactId1From0, 1, 0); assertGroupCount(c0, contactId1From0, 1, 0);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size()); assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 2, 1); assertGroupCount(c1, contactId0From1, 2, 0);
assertEquals(2, getMessageHeaders(c1, contactId0From1).size()); assertEquals(2, getMessageHeaders(c1, contactId0From1).size());
// When 1's timer has elapsed, the message should be deleted from 1's // When 1's timer has elapsed, the message should be deleted from 1's
@@ -266,7 +263,8 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
c1.getTimeTravel().addCurrentTimeMillis(1); c1.getTimeTravel().addCurrentTimeMillis(1);
assertGroupCount(c0, contactId1From0, 1, 0); assertGroupCount(c0, contactId1From0, 1, 0);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size()); assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 2, 1); // 1's total count is still 2, because of the added auto-decline
assertGroupCount(c1, contactId0From1, 2, 0);
forEachHeader(c1, contactId0From1, 2, h -> { forEachHeader(c1, contactId0From1, 2, h -> {
if (h instanceof GroupInvitationRequest) { if (h instanceof GroupInvitationRequest) {
// the request is for the first invitation // the request is for the first invitation
@@ -277,6 +275,7 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
GroupInvitationResponse r = (GroupInvitationResponse) h; GroupInvitationResponse r = (GroupInvitationResponse) h;
// is auto-decline for 2nd invitation // is auto-decline for 2nd invitation
assertEquals(privateGroup.getId(), r.getShareableId()); assertEquals(privateGroup.getId(), r.getShareableId());
assertTrue(r.isAutoDecline());
assertFalse(r.wasAccepted()); assertFalse(r.wasAccepted());
} }
}); });
@@ -288,34 +287,48 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
waitForEvents(c1); waitForEvents(c1);
// 0 marks the message as read - this starts 0's timer // 0 marks the message as read - this starts 0's timer
GroupInvitationResponse autoDeclineMessage = (GroupInvitationResponse) GroupInvitationResponse autoDeclineMessage = (GroupInvitationResponse)
getMessageHeaders(c1, contactId0From1).get(1); getMessageHeaders(c0, contactId1From0).get(1);
markMessageRead(c0, contact1From0, autoDeclineMessage.getId()); markMessageRead(c0, contact1From0, autoDeclineMessage.getId());
waitForEvents(c0);
assertGroupCount(c0, contactId1From0, 2, 0); assertGroupCount(c0, contactId1From0, 2, 0);
assertGroupCount(c1, contactId0From1, 2, 1); assertGroupCount(c1, contactId0From1, 2, 0);
// Timer of auto-decline elapses for both peers at the same time // Timer of auto-decline elapses for both peers at the same time
c0.getTimeTravel().addCurrentTimeMillis(timerLatency); c0.getTimeTravel().addCurrentTimeMillis(timerLatency);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency); c1.getTimeTravel().addCurrentTimeMillis(timerLatency);
assertGroupCount(c0, contactId1From0, 1, 0); assertGroupCount(c0, contactId1From0, 1, 0);
assertGroupCount(c1, contactId0From1, 1, 1); assertGroupCount(c1, contactId0From1, 1, 0);
// 1 responds to first invitation (that had no timer) // 1 responds to first invitation (that had no timer)
groupInvitationManager1.respondToInvitation(contactId0From1, pg, true); groupInvitationManager1.respondToInvitation(contactId0From1, pg, true);
// Sync the accept response message to 0 // Sync the accept response message to 0
sync1To0(1, true); sync1To0(1, true);
// Sync the ack to 1 - this starts 1's timer // Sync the ack (and creator's join messages (group + protocol) to 1
ack0To1(1); // this starts 1's timer
sync0To1(2, true);
waitForEvents(c1); waitForEvents(c1);
assertGroupCount(c0, contactId1From0, 2, 1); assertGroupCount(c0, contactId1From0, 2, 1);
assertGroupCount(c1, contactId0From1, 2, 1); assertGroupCount(c1, contactId0From1, 2, 0);
forEachHeader(c1, contactId0From1, 2, h -> {
if (h instanceof GroupInvitationRequest) {
// the request is for the first invitation
assertEquals(pg.getId(),
((GroupInvitationRequest) h).getNameable().getId());
} else {
assertTrue(h instanceof GroupInvitationResponse);
GroupInvitationResponse r = (GroupInvitationResponse) h;
// is accept for 1nd invitation
assertEquals(pg.getId(), r.getShareableId());
assertFalse(r.isAutoDecline());
assertTrue(r.wasAccepted());
}
});
// Before 1's timer elapses, 1 should still see the message // Before 1's timer elapses, 1 should still see the message
c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1); c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1); c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
assertGroupCount(c0, contactId1From0, 2, 1); assertGroupCount(c0, contactId1From0, 2, 1);
assertEquals(2, getMessageHeaders(c0, contactId1From0).size()); assertEquals(2, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 2, 1); assertGroupCount(c1, contactId0From1, 2, 0);
assertEquals(2, getMessageHeaders(c1, contactId0From1).size()); assertEquals(2, getMessageHeaders(c1, contactId0From1).size());
// When 1's timer has elapsed, the message should be deleted from 1's // When 1's timer has elapsed, the message should be deleted from 1's
@@ -324,12 +337,43 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
c1.getTimeTravel().addCurrentTimeMillis(1); c1.getTimeTravel().addCurrentTimeMillis(1);
assertGroupCount(c0, contactId1From0, 2, 1); assertGroupCount(c0, contactId1From0, 2, 1);
assertEquals(2, getMessageHeaders(c0, contactId1From0).size()); assertEquals(2, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 1); assertGroupCount(c1, contactId0From1, 1, 0);
forEachHeader(c1, contactId0From1, 1, h -> { forEachHeader(c1, contactId0From1, 1, h -> {
assertTrue(h instanceof GroupInvitationRequest); assertTrue(h instanceof GroupInvitationRequest);
assertTrue(((GroupInvitationRequest) h).wasAnswered()); assertTrue(((GroupInvitationRequest) h).wasAnswered());
assertTrue(((GroupInvitationRequest) h).canBeOpened()); assertTrue(((GroupInvitationRequest) h).canBeOpened());
}); });
// 0 reads all messages
forEachHeader(c0, contactId1From0, 2, h -> {
try {
if (!h.isRead()) markMessageRead(c0, contact1From0, h.getId());
} catch (Exception e) {
fail();
}
});
assertGroupCount(c0, contactId1From0, 2, 0);
// Before 0's timer elapses, 0 should still see the messages
c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
assertGroupCount(c0, contactId1From0, 2, 0);
assertGroupCount(c1, contactId0From1, 1, 0);
// When 0's timer has elapsed, the messages should be deleted from 0's
// view of the conversation, only the initial invitation remains
c0.getTimeTravel().addCurrentTimeMillis(1);
c1.getTimeTravel().addCurrentTimeMillis(1);
assertGroupCount(c0, contactId1From0, 1, 0);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 0);
assertEquals(1, getMessageHeaders(c1, contactId0From1).size());
// 1 joined the PrivateGroup
assertEquals(pg,
c1.getPrivateGroupManager().getPrivateGroup(pg.getId()));
assertFalse(groupInvitationManager0
.isInvitationAllowed(contact1From0, pg.getId()));
} }
@Test @Test
@@ -363,8 +407,8 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
// 1 responds to invitation // 1 responds to invitation
groupInvitationManager1 groupInvitationManager1
.respondToInvitation(contactId0From1, privateGroup, true); .respondToInvitation(contactId0From1, privateGroup, false);
// Sync the accept response message to 0 // Sync the decline response message to 0
sync1To0(1, true); sync1To0(1, true);
// Sync the ack to 1 - this starts 1's timer // Sync the ack to 1 - this starts 1's timer
ack0To1(1); ack0To1(1);
@@ -373,18 +417,19 @@ public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
assertGroupCount(c1, contactId0From1, 2, 0); assertGroupCount(c1, contactId0From1, 2, 0);
// 0 marks the message as read - this starts 0's timer // 0 marks the message as read - this starts 0's timer
GroupInvitationResponse autoDeclineMessage = (GroupInvitationResponse) GroupInvitationResponse message1 = (GroupInvitationResponse)
getMessageHeaders(c1, contactId0From1).get(1); getMessageHeaders(c0, contactId1From0).get(0);
markMessageRead(c0, contact1From0, autoDeclineMessage.getId()); markMessageRead(c0, contact1From0, message1.getId());
waitForEvents(c0);
assertGroupCount(c0, contactId1From0, 1, 0); assertGroupCount(c0, contactId1From0, 1, 0);
assertGroupCount(c1, contactId0From1, 2, 0); assertGroupCount(c1, contactId0From1, 2, 0);
// 1 joined the PrivateGroup // both peers delete all messages after their timers expire
assertEquals(privateGroup, c1.getPrivateGroupManager() c0.getTimeTravel().addCurrentTimeMillis(timerLatency);
.getPrivateGroup(privateGroup.getId())); c1.getTimeTravel().addCurrentTimeMillis(timerLatency);
assertFalse(groupInvitationManager0 assertGroupCount(c0, contactId1From0, 0, 0);
.isInvitationAllowed(contact1From0, privateGroup.getId())); assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 0, 0);
assertEquals(0, getMessageHeaders(c1, contactId0From1).size());
} }
private PrivateGroup addPrivateGroup(String name, long timestamp) private PrivateGroup addPrivateGroup(String name, long timestamp)

View File

@@ -123,19 +123,19 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
@Test @Test
public void testOnLeaveActionFromStart() throws Exception { public void testOnLeaveActionFromStart() throws Exception {
CreatorSession session = getDefaultSession(START); CreatorSession session = getDefaultSession(START);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromDissolved() throws Exception { public void testOnLeaveActionFromDissolved() throws Exception {
CreatorSession session = getDefaultSession(DISSOLVED); CreatorSession session = getDefaultSession(DISSOLVED);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromError() throws Exception { public void testOnLeaveActionFromError() throws Exception {
CreatorSession session = getDefaultSession(ERROR); CreatorSession session = getDefaultSession(ERROR);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
@@ -143,7 +143,7 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
CreatorSession session = getDefaultSession(INVITED); CreatorSession session = getDefaultSession(INVITED);
expectOnLocalLeave(); expectOnLocalLeave();
CreatorSession newSession = engine.onLeaveAction(txn, session); CreatorSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(DISSOLVED, newSession.getState()); assertEquals(DISSOLVED, newSession.getState());
assertEquals(messageId, newSession.getLastLocalMessageId()); assertEquals(messageId, newSession.getLastLocalMessageId());
assertEquals(lastRemoteMessageId, newSession.getLastRemoteMessageId()); assertEquals(lastRemoteMessageId, newSession.getLastRemoteMessageId());
@@ -157,7 +157,7 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
CreatorSession session = getDefaultSession(JOINED); CreatorSession session = getDefaultSession(JOINED);
expectOnLocalLeave(); expectOnLocalLeave();
CreatorSession newSession = engine.onLeaveAction(txn, session); CreatorSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(DISSOLVED, newSession.getState()); assertEquals(DISSOLVED, newSession.getState());
assertEquals(messageId, newSession.getLastLocalMessageId()); assertEquals(messageId, newSession.getLastLocalMessageId());
assertEquals(lastRemoteMessageId, newSession.getLastRemoteMessageId()); assertEquals(lastRemoteMessageId, newSession.getLastRemoteMessageId());
@@ -171,7 +171,7 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
CreatorSession session = getDefaultSession(LEFT); CreatorSession session = getDefaultSession(LEFT);
expectOnLocalLeave(); expectOnLocalLeave();
CreatorSession newSession = engine.onLeaveAction(txn, session); CreatorSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(DISSOLVED, newSession.getState()); assertEquals(DISSOLVED, newSession.getState());
assertEquals(messageId, newSession.getLastLocalMessageId()); assertEquals(messageId, newSession.getLastLocalMessageId());
assertEquals(lastRemoteMessageId, newSession.getLastRemoteMessageId()); assertEquals(lastRemoteMessageId, newSession.getLastRemoteMessageId());

View File

@@ -593,7 +593,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
.parseInviteeSession(contactGroup.getId(), bdfSession); .parseInviteeSession(contactGroup.getId(), bdfSession);
will(returnValue(inviteeSession)); will(returnValue(inviteeSession));
if (accept) oneOf(inviteeEngine).onJoinAction(txn, inviteeSession); if (accept) oneOf(inviteeEngine).onJoinAction(txn, inviteeSession);
else oneOf(inviteeEngine).onLeaveAction(txn, inviteeSession); else oneOf(inviteeEngine).onLeaveAction(txn, inviteeSession, false);
will(returnValue(inviteeSession)); will(returnValue(inviteeSession));
}}); }});
expectStoreSession(inviteeSession, storageMessage.getId()); expectStoreSession(inviteeSession, storageMessage.getId());
@@ -653,10 +653,10 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
long time1 = 1L, time2 = 2L; long time1 = 1L, time2 = 2L;
MessageMetadata messageMetadata1 = MessageMetadata messageMetadata1 =
new MessageMetadata(INVITE, privateGroup.getId(), time1, true, new MessageMetadata(INVITE, privateGroup.getId(), time1, true,
true, true, false, true, NO_AUTO_DELETE_TIMER); true, true, false, true, NO_AUTO_DELETE_TIMER, false);
MessageMetadata messageMetadata2 = MessageMetadata messageMetadata2 =
new MessageMetadata(JOIN, privateGroup.getId(), time2, true, new MessageMetadata(JOIN, privateGroup.getId(), time2, true,
true, true, true, false, NO_AUTO_DELETE_TIMER); true, true, true, false, NO_AUTO_DELETE_TIMER, false);
InviteMessage invite = InviteMessage invite =
new InviteMessage(message.getId(), contactGroup.getId(), new InviteMessage(message.getId(), contactGroup.getId(),
privateGroup.getId(), time1, "name", author, privateGroup.getId(), time1, "name", author,
@@ -876,7 +876,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
oneOf(sessionParser) oneOf(sessionParser)
.parseCreatorSession(contactGroup.getId(), bdfSession); .parseCreatorSession(contactGroup.getId(), bdfSession);
will(returnValue(creatorSession)); will(returnValue(creatorSession));
oneOf(creatorEngine).onLeaveAction(txn, creatorSession); oneOf(creatorEngine).onLeaveAction(txn, creatorSession, false);
will(returnValue(creatorSession)); will(returnValue(creatorSession));
// session 2 // session 2
oneOf(sessionParser).getRole(bdfSession2); oneOf(sessionParser).getRole(bdfSession2);
@@ -884,7 +884,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
oneOf(sessionParser) oneOf(sessionParser)
.parseInviteeSession(contactGroup2.getId(), bdfSession2); .parseInviteeSession(contactGroup2.getId(), bdfSession2);
will(returnValue(inviteeSession)); will(returnValue(inviteeSession));
oneOf(inviteeEngine).onLeaveAction(txn, inviteeSession); oneOf(inviteeEngine).onLeaveAction(txn, inviteeSession, false);
will(returnValue(inviteeSession)); will(returnValue(inviteeSession));
// session 3 // session 3
oneOf(sessionParser).getRole(bdfSession3); oneOf(sessionParser).getRole(bdfSession3);
@@ -892,7 +892,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
oneOf(sessionParser) oneOf(sessionParser)
.parsePeerSession(contactGroup3.getId(), bdfSession3); .parsePeerSession(contactGroup3.getId(), bdfSession3);
will(returnValue(peerSession)); will(returnValue(peerSession));
oneOf(peerEngine).onLeaveAction(txn, peerSession); oneOf(peerEngine).onLeaveAction(txn, peerSession, false);
will(returnValue(peerSession)); will(returnValue(peerSession));
}}); }});

View File

@@ -636,8 +636,7 @@ public class GroupInvitationValidatorTest extends ValidatorTestCase {
long autoDeleteTimer, BdfDictionary metadata) { long autoDeleteTimer, BdfDictionary metadata) {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageEncoder).encodeMetadata(type, message.getGroupId(), oneOf(messageEncoder).encodeMetadata(type, message.getGroupId(),
message.getTimestamp(), false, false, false, false, false, message.getTimestamp(), autoDeleteTimer);
autoDeleteTimer);
will(returnValue(metadata)); will(returnValue(metadata));
}}); }});
} }

View File

@@ -193,25 +193,25 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
@Test @Test
public void testOnLeaveActionFromStart() throws Exception { public void testOnLeaveActionFromStart() throws Exception {
InviteeSession session = getDefaultSession(START); InviteeSession session = getDefaultSession(START);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromLeft() throws Exception { public void testOnLeaveActionFromLeft() throws Exception {
InviteeSession session = getDefaultSession(LEFT); InviteeSession session = getDefaultSession(LEFT);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromDissolved() throws Exception { public void testOnLeaveActionFromDissolved() throws Exception {
InviteeSession session = getDefaultSession(DISSOLVED); InviteeSession session = getDefaultSession(DISSOLVED);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromError() throws Exception { public void testOnLeaveActionFromError() throws Exception {
InviteeSession session = getDefaultSession(ERROR); InviteeSession session = getDefaultSession(ERROR);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
@@ -223,7 +223,7 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
}}); }});
InviteeSession session = getDefaultSession(INVITED); InviteeSession session = getDefaultSession(INVITED);
InviteeSession newSession = engine.onLeaveAction(txn, session); InviteeSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(START, newSession.getState()); assertEquals(START, newSession.getState());
assertSessionRecordedSentMessage(newSession); assertSessionRecordedSentMessage(newSession);
@@ -245,7 +245,7 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
expectSendLeaveMessage(false); expectSendLeaveMessage(false);
expectSetPrivateGroupVisibility(INVISIBLE); expectSetPrivateGroupVisibility(INVISIBLE);
InviteeSession session = getDefaultSession(ACCEPTED); InviteeSession session = getDefaultSession(ACCEPTED);
InviteeSession newSession = engine.onLeaveAction(txn, session); InviteeSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(LEFT, newSession.getState()); assertEquals(LEFT, newSession.getState());
assertSessionRecordedSentMessage(newSession); assertSessionRecordedSentMessage(newSession);
@@ -257,7 +257,7 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
expectSendLeaveMessage(false); expectSendLeaveMessage(false);
expectSetPrivateGroupVisibility(INVISIBLE); expectSetPrivateGroupVisibility(INVISIBLE);
InviteeSession session = getDefaultSession(JOINED); InviteeSession session = getDefaultSession(JOINED);
InviteeSession newSession = engine.onLeaveAction(txn, session); InviteeSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(LEFT, newSession.getState()); assertEquals(LEFT, newSession.getState());
assertSessionRecordedSentMessage(newSession); assertSessionRecordedSentMessage(newSession);

View File

@@ -145,31 +145,31 @@ public class PeerProtocolEngineTest extends AbstractProtocolEngineTest {
@Test @Test
public void testOnLeaveActionFromStart() throws Exception { public void testOnLeaveActionFromStart() throws Exception {
PeerSession session = getDefaultSession(START); PeerSession session = getDefaultSession(START);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromAwaitMember() throws Exception { public void testOnLeaveActionFromAwaitMember() throws Exception {
PeerSession session = getDefaultSession(AWAIT_MEMBER); PeerSession session = getDefaultSession(AWAIT_MEMBER);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromNeitherJoined() throws Exception { public void testOnLeaveActionFromNeitherJoined() throws Exception {
PeerSession session = getDefaultSession(NEITHER_JOINED); PeerSession session = getDefaultSession(NEITHER_JOINED);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromLocalLeft() throws Exception { public void testOnLeaveActionFromLocalLeft() throws Exception {
PeerSession session = getDefaultSession(LOCAL_LEFT); PeerSession session = getDefaultSession(LOCAL_LEFT);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
public void testOnLeaveActionFromError() throws Exception { public void testOnLeaveActionFromError() throws Exception {
PeerSession session = getDefaultSession(ERROR); PeerSession session = getDefaultSession(ERROR);
assertEquals(session, engine.onLeaveAction(txn, session)); assertEquals(session, engine.onLeaveAction(txn, session, false));
} }
@Test @Test
@@ -178,7 +178,7 @@ public class PeerProtocolEngineTest extends AbstractProtocolEngineTest {
expectSendLeaveMessage(false); expectSendLeaveMessage(false);
expectSetPrivateGroupVisibility(INVISIBLE); expectSetPrivateGroupVisibility(INVISIBLE);
PeerSession newSession = engine.onLeaveAction(txn, session); PeerSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(NEITHER_JOINED, newSession.getState()); assertEquals(NEITHER_JOINED, newSession.getState());
assertSessionRecordedSentMessage(newSession); assertSessionRecordedSentMessage(newSession);
@@ -191,7 +191,7 @@ public class PeerProtocolEngineTest extends AbstractProtocolEngineTest {
expectSendLeaveMessage(false); expectSendLeaveMessage(false);
expectSetPrivateGroupVisibility(INVISIBLE); expectSetPrivateGroupVisibility(INVISIBLE);
PeerSession newSession = engine.onLeaveAction(txn, session); PeerSession newSession = engine.onLeaveAction(txn, session, false);
assertEquals(LOCAL_LEFT, newSession.getState()); assertEquals(LOCAL_LEFT, newSession.getState());
assertSessionRecordedSentMessage(newSession); assertSessionRecordedSentMessage(newSession);