Merge branch '1835-auto-decline-self-destructed-incoming-blog-forum-sharing' into '804-self-destructing-messages'

Automatically decline incoming blog/forum invitations when they self-destruct

See merge request briar/briar!1392
This commit is contained in:
Torsten Grote
2021-03-16 11:23:41 +00:00
23 changed files with 650 additions and 45 deletions

View File

@@ -102,6 +102,10 @@ class ConversationVisitor implements
text = ctx.getString( text = ctx.getString(
R.string.blogs_sharing_response_accepted_sent, R.string.blogs_sharing_response_accepted_sent,
contactName.getValue()); contactName.getValue());
} else if (r.isAutoDecline()) {
text = ctx.getString(
R.string.blogs_sharing_response_declined_auto,
contactName.getValue());
} else { } else {
text = ctx.getString( text = ctx.getString(
R.string.blogs_sharing_response_declined_sent, R.string.blogs_sharing_response_declined_sent,
@@ -155,6 +159,10 @@ class ConversationVisitor implements
text = ctx.getString( text = ctx.getString(
R.string.forum_invitation_response_accepted_sent, R.string.forum_invitation_response_accepted_sent,
contactName.getValue()); contactName.getValue());
} else if (r.isAutoDecline()) {
text = ctx.getString(
R.string.forum_invitation_response_declined_auto,
contactName.getValue());
} else { } else {
text = ctx.getString( text = ctx.getString(
R.string.forum_invitation_response_declined_sent, R.string.forum_invitation_response_declined_sent,

View File

@@ -419,6 +419,7 @@
<string name="forum_invitation_already_sharing">Already sharing</string> <string name="forum_invitation_already_sharing">Already sharing</string>
<string name="forum_invitation_response_accepted_sent">You accepted the forum invitation from %s.</string> <string name="forum_invitation_response_accepted_sent">You accepted the forum invitation from %s.</string>
<string name="forum_invitation_response_declined_sent">You declined the forum invitation from %s.</string> <string name="forum_invitation_response_declined_sent">You declined the forum invitation from %s.</string>
<string name="forum_invitation_response_declined_auto">The forum invitation from %s was automatically declined.</string>
<string name="forum_invitation_response_accepted_received">%s accepted the forum invitation.</string> <string name="forum_invitation_response_accepted_received">%s accepted the forum invitation.</string>
<string name="forum_invitation_response_declined_received">%s declined the forum invitation.</string> <string name="forum_invitation_response_declined_received">%s declined the forum invitation.</string>
@@ -456,6 +457,7 @@
<string name="blogs_sharing_snackbar">Blog shared with chosen contacts</string> <string name="blogs_sharing_snackbar">Blog shared with chosen contacts</string>
<string name="blogs_sharing_response_accepted_sent">You accepted the blog invitation from %s.</string> <string name="blogs_sharing_response_accepted_sent">You accepted the blog invitation from %s.</string>
<string name="blogs_sharing_response_declined_sent">You declined the blog invitation from %s.</string> <string name="blogs_sharing_response_declined_sent">You declined the blog invitation from %s.</string>
<string name="blogs_sharing_response_declined_auto">The blog invitation from %s was automatically declined.</string>
<string name="blogs_sharing_response_accepted_received">%s accepted the blog invitation.</string> <string name="blogs_sharing_response_accepted_received">%s accepted the blog invitation.</string>
<string name="blogs_sharing_response_declined_received">%s declined the blog invitation.</string> <string name="blogs_sharing_response_declined_received">%s declined the blog invitation.</string>
<string name="blogs_sharing_invitation_received">%1$s has shared the blog \"%2$s\" with you.</string> <string name="blogs_sharing_invitation_received">%1$s has shared the blog \"%2$s\" with you.</string>

View File

@@ -13,9 +13,9 @@ public class BlogInvitationResponse extends InvitationResponse {
public BlogInvitationResponse(MessageId id, GroupId groupId, long time, public BlogInvitationResponse(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, false); accept, shareableId, autoDeleteTimer, isAutoDecline);
} }
@Override @Override

View File

@@ -16,9 +16,9 @@ public class ForumInvitationResponse extends InvitationResponse {
public ForumInvitationResponse(MessageId id, GroupId groupId, long time, public ForumInvitationResponse(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, false); accept, shareableId, autoDeleteTimer, isAutoDecline);
} }
@Override @Override

View File

@@ -33,11 +33,11 @@ public class BlogInvitationFactoryImpl
public BlogInvitationResponse createInvitationResponse(MessageId id, public BlogInvitationResponse createInvitationResponse(MessageId id,
GroupId contactGroupId, long time, boolean local, boolean sent, GroupId contactGroupId, long time, boolean local, boolean sent,
boolean seen, boolean read, boolean accept, GroupId shareableId, boolean seen, boolean read, boolean accept, GroupId shareableId,
long autoDeleteTimer) { long autoDeleteTimer, boolean isAutoDecline) {
SessionId sessionId = new SessionId(shareableId.getBytes()); SessionId sessionId = new SessionId(shareableId.getBytes());
return new BlogInvitationResponse(id, contactGroupId, time, local, read, return new BlogInvitationResponse(id, contactGroupId, time, local, read,
sent, seen, sessionId, accept, shareableId, sent, seen, sessionId, accept, shareableId,
autoDeleteTimer); autoDeleteTimer, isAutoDecline);
} }
} }

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
@@ -71,7 +72,8 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
BlogInvitationResponse response = invitationFactory BlogInvitationResponse response = invitationFactory
.createInvitationResponse(m.getId(), m.getContactGroupId(), .createInvitationResponse(m.getId(), m.getContactGroupId(),
m.getTimestamp(), false, false, false, false, m.getTimestamp(), false, false, false, false,
true, m.getShareableId(), m.getAutoDeleteTimer()); true, m.getShareableId(), m.getAutoDeleteTimer(),
false);
return new BlogInvitationResponseReceivedEvent(response, contactId); return new BlogInvitationResponseReceivedEvent(response, contactId);
} }
@@ -81,7 +83,18 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
BlogInvitationResponse response = invitationFactory BlogInvitationResponse response = invitationFactory
.createInvitationResponse(m.getId(), m.getContactGroupId(), .createInvitationResponse(m.getId(), m.getContactGroupId(),
m.getTimestamp(), false, false, false, false, m.getTimestamp(), false, false, false, false,
false, m.getShareableId(), m.getAutoDeleteTimer()); false, m.getShareableId(), m.getAutoDeleteTimer(),
false);
return new BlogInvitationResponseReceivedEvent(response, contactId);
}
@Override
Event getAutoDeclineInvitationResponseReceivedEvent(Session s, Message m,
ContactId contactId, long timer) {
BlogInvitationResponse response = invitationFactory
.createInvitationResponse(m.getId(), s.getContactGroupId(),
m.getTimestamp(), true, false, false, true,
false, s.getShareableId(), timer, true);
return new BlogInvitationResponseReceivedEvent(response, contactId); return new BlogInvitationResponseReceivedEvent(response, contactId);
} }

View File

@@ -33,11 +33,11 @@ public class ForumInvitationFactoryImpl
public ForumInvitationResponse createInvitationResponse(MessageId id, public ForumInvitationResponse createInvitationResponse(MessageId id,
GroupId contactGroupId, long time, boolean local, boolean sent, GroupId contactGroupId, long time, boolean local, boolean sent,
boolean seen, boolean read, boolean accept, GroupId shareableId, boolean seen, boolean read, boolean accept, GroupId shareableId,
long autoDeleteTimer) { long autoDeleteTimer, boolean isAutoDecline) {
SessionId sessionId = new SessionId(shareableId.getBytes()); SessionId sessionId = new SessionId(shareableId.getBytes());
return new ForumInvitationResponse(id, contactGroupId, time, local, return new ForumInvitationResponse(id, contactGroupId, time, local,
read, sent, seen, sessionId, accept, shareableId, read, sent, seen, sessionId, accept, shareableId,
autoDeleteTimer); autoDeleteTimer, isAutoDecline);
} }
} }

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
@@ -71,7 +72,8 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
ForumInvitationResponse response = invitationFactory ForumInvitationResponse response = invitationFactory
.createInvitationResponse(m.getId(), m.getContactGroupId(), .createInvitationResponse(m.getId(), m.getContactGroupId(),
m.getTimestamp(), false, false, true, false, m.getTimestamp(), false, false, true, false,
true, m.getShareableId(), m.getAutoDeleteTimer()); true, m.getShareableId(), m.getAutoDeleteTimer(),
false);
return new ForumInvitationResponseReceivedEvent(response, contactId); return new ForumInvitationResponseReceivedEvent(response, contactId);
} }
@@ -81,7 +83,18 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
ForumInvitationResponse response = invitationFactory ForumInvitationResponse response = invitationFactory
.createInvitationResponse(m.getId(), m.getContactGroupId(), .createInvitationResponse(m.getId(), m.getContactGroupId(),
m.getTimestamp(), false, false, true, false, m.getTimestamp(), false, false, true, false,
false, m.getShareableId(), m.getAutoDeleteTimer()); false, m.getShareableId(), m.getAutoDeleteTimer(),
false);
return new ForumInvitationResponseReceivedEvent(response, contactId);
}
@Override
Event getAutoDeclineInvitationResponseReceivedEvent(Session s, Message m,
ContactId contactId, long timer) {
ForumInvitationResponse response = invitationFactory
.createInvitationResponse(m.getId(), s.getContactGroupId(),
m.getTimestamp(), true, false, false, true,
false, s.getShareableId(), timer, true);
return new ForumInvitationResponseReceivedEvent(response, contactId); return new ForumInvitationResponseReceivedEvent(response, contactId);
} }

View File

@@ -15,6 +15,7 @@ public interface InvitationFactory<S extends Shareable, R extends InvitationResp
R createInvitationResponse(MessageId id, GroupId contactGroupId, long time, R createInvitationResponse(MessageId id, GroupId contactGroupId, long time,
boolean local, boolean sent, boolean seen, boolean read, boolean local, boolean sent, boolean seen, boolean read,
boolean accept, GroupId shareableId, long autoDeleteTimer); boolean accept, GroupId shareableId, long autoDeleteTimer,
boolean isAutoDecline);
} }

View File

@@ -14,7 +14,8 @@ interface MessageEncoder {
BdfDictionary encodeMetadata(MessageType type, GroupId shareableId, BdfDictionary encodeMetadata(MessageType type, GroupId shareableId,
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);
void setVisibleInUi(BdfDictionary meta, boolean visible); void setVisibleInUi(BdfDictionary meta, boolean visible);

View File

@@ -23,6 +23,7 @@ import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AUTO_DELETE_TIMER; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_INVITATION_ACCEPTED; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_IS_AUTO_DECLINE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_READ; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_READ;
@@ -48,7 +49,7 @@ class MessageEncoderImpl implements MessageEncoder {
public BdfDictionary encodeMetadata(MessageType type, public BdfDictionary encodeMetadata(MessageType type,
GroupId shareableId, long timestamp, boolean local, boolean read, GroupId shareableId, 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_SHAREABLE_ID, shareableId); meta.put(MSG_KEY_SHAREABLE_ID, shareableId);
@@ -61,6 +62,9 @@ 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;
} }

View File

@@ -13,10 +13,11 @@ class MessageMetadata {
private final GroupId shareableId; private final GroupId shareableId;
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 shareableId, long timestamp, MessageMetadata(MessageType type, GroupId shareableId, long timestamp,
boolean local, boolean read, boolean visible, boolean available, boolean local, boolean read, boolean visible, boolean available,
boolean accepted, long autoDeleteTimer) { boolean accepted, long autoDeleteTimer, boolean isAutoDecline) {
this.shareableId = shareableId; this.shareableId = shareableId;
this.type = type; this.type = type;
this.timestamp = timestamp; this.timestamp = timestamp;
@@ -26,6 +27,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 +60,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 +70,8 @@ class MessageMetadata {
public long getAutoDeleteTimer() { public long getAutoDeleteTimer() {
return autoDeleteTimer; return autoDeleteTimer;
} }
public boolean isAutoDecline() {
return isAutoDecline;
}
} }

View File

@@ -20,6 +20,7 @@ import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AUTO_DELETE_TIMER; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_INVITATION_ACCEPTED; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_IS_AUTO_DECLINE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_READ; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_READ;
@@ -74,8 +75,9 @@ abstract class MessageParserImpl<S extends Shareable>
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, shareableId, timestamp, local, read, return new MessageMetadata(type, shareableId, timestamp, local, read,
visible, available, accepted, timer); visible, available, accepted, timer, isAutoDecline);
} }
@Override @Override

View File

@@ -16,8 +16,8 @@ interface ProtocolEngine<S extends Shareable> {
Session onAcceptAction(Transaction txn, Session session) throws DbException; Session onAcceptAction(Transaction txn, Session session) throws DbException;
Session onDeclineAction(Transaction txn, Session session) Session onDeclineAction(Transaction txn, Session session,
throws DbException; boolean isAutoDecline) throws DbException;
Session onLeaveAction(Transaction txn, Session session) throws DbException; Session onLeaveAction(Transaction txn, Session session) throws DbException;

View File

@@ -148,6 +148,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
localTimestamp, s.getLastLocalMessageId(), descriptor, localTimestamp, s.getLastLocalMessageId(), descriptor,
text, timer); text, timer);
sendMessage(txn, m, INVITE, s.getShareableId(), true, timer); sendMessage(txn, m, INVITE, s.getShareableId(), true, timer);
// Set the auto-delete timer duration on the message
if (timer != NO_AUTO_DELETE_TIMER) {
db.setCleanupTimerDuration(txn, m.getId(), timer);
}
} else { } else {
m = messageEncoder.encodeInviteMessage(s.getContactGroupId(), m = messageEncoder.encodeInviteMessage(s.getContactGroupId(),
localTimestamp, s.getLastLocalMessageId(), descriptor, localTimestamp, s.getLastLocalMessageId(), descriptor,
@@ -216,6 +220,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
s.getShareableId(), localTimestamp, s.getShareableId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);
sendMessage(txn, m, ACCEPT, s.getShareableId(), true, timer); sendMessage(txn, m, ACCEPT, s.getShareableId(), true, timer);
// Set the auto-delete timer duration on the message
if (timer != NO_AUTO_DELETE_TIMER) {
db.setCleanupTimerDuration(txn, m.getId(), timer);
}
} else { } else {
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(), m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
s.getShareableId(), localTimestamp, s.getShareableId(), localTimestamp,
@@ -227,11 +235,11 @@ abstract class ProtocolEngineImpl<S extends Shareable>
} }
@Override @Override
public Session onDeclineAction(Transaction txn, Session s) public Session onDeclineAction(Transaction txn, Session s,
throws DbException { boolean isAutoDecline) throws DbException {
switch (s.getState()) { switch (s.getState()) {
case LOCAL_INVITED: case LOCAL_INVITED:
return onLocalDecline(txn, s); return onLocalDecline(txn, s, isAutoDecline);
case START: case START:
case REMOTE_INVITED: case REMOTE_INVITED:
case SHARING: case SHARING:
@@ -243,14 +251,14 @@ abstract class ProtocolEngineImpl<S extends Shareable>
} }
} }
private Session onLocalDecline(Transaction txn, Session s) private Session onLocalDecline(Transaction txn, Session 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 DECLINE message // Send a DECLINE message
Message sent = sendDeclineMessage(txn, s); Message sent = sendDeclineMessage(txn, s, 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
@@ -259,8 +267,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
s.getInviteTimestamp()); s.getInviteTimestamp());
} }
private Message sendDeclineMessage(Transaction txn, Session s) private Message sendDeclineMessage(Transaction txn, Session s,
throws DbException { boolean isAutoDecline) throws DbException {
Message m; Message m;
long localTimestamp = getTimestampForVisibleMessage(txn, s); long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
@@ -270,7 +278,18 @@ abstract class ProtocolEngineImpl<S extends Shareable>
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(), m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
s.getShareableId(), localTimestamp, s.getShareableId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);
sendMessage(txn, m, DECLINE, s.getShareableId(), true, timer); sendMessage(txn, m, DECLINE, s.getShareableId(), true, timer,
isAutoDecline);
// Set the auto-delete timer duration on the local message
if (timer != NO_AUTO_DELETE_TIMER) {
db.setCleanupTimerDuration(txn, m.getId(), timer);
}
if (isAutoDecline) {
// Broadcast an event, so the auto-decline becomes visible
Event e = getAutoDeclineInvitationResponseReceivedEvent(
s, m, c, timer);
txn.attach(e);
}
} else { } else {
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(), m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
s.getShareableId(), localTimestamp, s.getShareableId(), localTimestamp,
@@ -281,6 +300,9 @@ abstract class ProtocolEngineImpl<S extends Shareable>
return m; return m;
} }
abstract Event getAutoDeclineInvitationResponseReceivedEvent(Session s,
Message m, ContactId contactId, long timer);
@Override @Override
public Session onLeaveAction(Transaction txn, Session s) public Session onLeaveAction(Transaction txn, Session s)
throws DbException { throws DbException {
@@ -394,8 +416,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
// Broadcast an event // Broadcast an event
ContactId contactId = ContactId contactId =
clientHelper.getContactId(txn, s.getContactGroupId()); clientHelper.getContactId(txn, s.getContactGroupId());
txn.attach( txn.attach(getInvitationRequestReceivedEvent(m, contactId, false,
getInvitationRequestReceivedEvent(m, contactId, false, true)); true));
// Move to the next state // Move to the next state
return new Session(SHARING, s.getContactGroupId(), s.getShareableId(), return new Session(SHARING, s.getContactGroupId(), s.getShareableId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(), s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
@@ -636,9 +658,16 @@ abstract class ProtocolEngineImpl<S extends Shareable>
private void sendMessage(Transaction txn, Message m, MessageType type, private void sendMessage(Transaction txn, Message m, MessageType type,
GroupId shareableId, boolean visibleInConversation, GroupId shareableId, boolean visibleInConversation,
long autoDeleteTimer) throws DbException { long autoDeleteTimer) throws DbException {
sendMessage(txn, m, type, shareableId, visibleInConversation,
autoDeleteTimer, false);
}
private void sendMessage(Transaction txn, Message m, MessageType type,
GroupId shareableId, boolean visibleInConversation,
long autoDeleteTimer, boolean isAutoDecline) throws DbException {
BdfDictionary meta = messageEncoder.encodeMetadata(type, shareableId, BdfDictionary meta = messageEncoder.encodeMetadata(type, shareableId,
m.getTimestamp(), true, true, visibleInConversation, false, m.getTimestamp(), true, true, visibleInConversation, false,
false, autoDeleteTimer); 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

@@ -14,6 +14,7 @@ interface SharingConstants {
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

@@ -1,6 +1,7 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.cleanup.CleanupHook;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
@@ -24,6 +25,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook; import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationMessageHeader; import org.briarproject.briar.api.conversation.ConversationMessageHeader;
@@ -47,18 +49,20 @@ import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.sharing.MessageType.ABORT; import static org.briarproject.briar.sharing.MessageType.ABORT;
import static org.briarproject.briar.sharing.MessageType.ACCEPT; import static org.briarproject.briar.sharing.MessageType.ACCEPT;
import static org.briarproject.briar.sharing.MessageType.DECLINE; import static org.briarproject.briar.sharing.MessageType.DECLINE;
import static org.briarproject.briar.sharing.MessageType.INVITE; import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.sharing.MessageType.LEAVE; import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.briarproject.briar.sharing.State.LOCAL_INVITED;
import static org.briarproject.briar.sharing.State.SHARING; import static org.briarproject.briar.sharing.State.SHARING;
@NotNullByDefault @NotNullByDefault
abstract class SharingManagerImpl<S extends Shareable> abstract class SharingManagerImpl<S extends Shareable>
extends ConversationClientImpl extends ConversationClientImpl
implements SharingManager<S>, OpenDatabaseHook, ContactHook, implements SharingManager<S>, OpenDatabaseHook, ContactHook,
ClientVersioningHook { ClientVersioningHook, CleanupHook {
private final ClientVersioningManager clientVersioningManager; private final ClientVersioningManager clientVersioningManager;
private final MessageParser<S> messageParser; private final MessageParser<S> messageParser;
@@ -133,6 +137,11 @@ abstract class SharingManagerImpl<S extends Shareable>
BdfDictionary d) throws DbException, FormatException { BdfDictionary d) throws DbException, FormatException {
// Parse the metadata // Parse the metadata
MessageMetadata meta = messageParser.parseMetadata(d); MessageMetadata meta = messageParser.parseMetadata(d);
// set the clean-up timer that will be started when message gets read
long timer = meta.getAutoDeleteTimer();
if (timer != NO_AUTO_DELETE_TIMER) {
db.setCleanupTimerDuration(txn, m.getId(), timer);
}
// Look up the session, if there is one // Look up the session, if there is one
SessionId sessionId = getSessionId(meta.getShareableId()); SessionId sessionId = getSessionId(meta.getShareableId());
StoredSession ss = getSession(txn, m.getGroupId(), sessionId); StoredSession ss = getSession(txn, m.getGroupId(), sessionId);
@@ -293,7 +302,13 @@ abstract class SharingManagerImpl<S extends Shareable>
@Override @Override
public void respondToInvitation(ContactId c, SessionId id, boolean accept) public void respondToInvitation(ContactId c, SessionId id, boolean accept)
throws DbException { throws DbException {
Transaction txn = db.startTransaction(false); db.transaction(false,
txn -> respondToInvitation(txn, c, id, accept, false));
}
private void respondToInvitation(Transaction txn, ContactId c,
SessionId id, 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);
@@ -305,14 +320,11 @@ abstract class SharingManagerImpl<S extends Shareable>
sessionParser.parseSession(contactGroupId, ss.bdfSession); sessionParser.parseSession(contactGroupId, ss.bdfSession);
// Handle the accept or decline action // Handle the accept or decline action
if (accept) session = engine.onAcceptAction(txn, session); if (accept) session = engine.onAcceptAction(txn, session);
else session = engine.onDeclineAction(txn, session); else session = engine.onDeclineAction(txn, session, isAutoDecline);
// Store the updated session // Store the updated session
storeSession(txn, ss.storageId, session); storeSession(txn, ss.storageId, session);
db.commitTransaction(txn);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} finally {
db.endTransaction(txn);
} }
} }
@@ -371,7 +383,7 @@ abstract class SharingManagerImpl<S extends Shareable>
return invitationFactory.createInvitationResponse(m, contactGroupId, return invitationFactory.createInvitationResponse(m, contactGroupId,
meta.getTimestamp(), meta.isLocal(), status.isSent(), meta.getTimestamp(), meta.isLocal(), status.isSent(),
status.isSeen(), meta.isRead(), accept, meta.getShareableId(), status.isSeen(), meta.isRead(), accept, meta.getShareableId(),
meta.getAutoDeleteTimer()); meta.getAutoDeleteTimer(), meta.isAutoDecline());
} }
@Override @Override
@@ -684,6 +696,57 @@ abstract class SharingManagerImpl<S extends Shareable>
return result; return result;
} }
@Override
public void deleteMessages(Transaction txn, GroupId g,
Collection<MessageId> messageIds) throws DbException {
ContactId c;
Map<SessionId, DeletableSession> sessions = new HashMap<>();
try {
// get the ContactId from the given GroupId
c = clientHelper.getContactId(txn, g);
// get sessions for all messages to be deleted
for (MessageId messageId : messageIds) {
BdfDictionary d = clientHelper
.getMessageMetadataAsDictionary(txn, messageId);
MessageMetadata messageMetadata =
messageParser.parseMetadata(d);
if (!messageMetadata.isVisibleInConversation())
throw new IllegalArgumentException();
SessionId sessionId =
getSessionId(messageMetadata.getShareableId());
DeletableSession deletableSession = sessions.get(sessionId);
if (deletableSession == null) {
StoredSession ss = getSession(txn, g, sessionId);
if (ss == null) throw new DbException();
Session session = sessionParser
.parseSession(g, ss.bdfSession);
deletableSession = new DeletableSession(session.getState());
sessions.put(sessionId, deletableSession);
}
deletableSession.messages.add(messageId);
}
} catch (FormatException e) {
throw new DbException(e);
}
// delete given visible messages in sessions
for (Entry<SessionId, DeletableSession> entry : sessions.entrySet()) {
DeletableSession session = entry.getValue();
// first decline pending invitation to shareable
if (session.state == LOCAL_INVITED) {
// marked as autoDecline
respondToInvitation(txn, c, entry.getKey(), false, true);
}
for (MessageId m : session.messages) {
db.deleteMessage(txn, m);
db.deleteMessageMetadata(txn, m);
}
}
recalculateGroupCount(txn, g);
txn.attach(new ConversationMessagesDeletedEvent(c, messageIds));
}
@Override @Override
public Set<MessageId> getMessageIds(Transaction txn, ContactId c) public Set<MessageId> getMessageIds(Transaction txn, ContactId c)
throws DbException { throws DbException {

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.cleanup.CleanupManager;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
@@ -75,7 +76,8 @@ public class SharingModule {
ValidationManager validationManager, ValidationManager validationManager,
ConversationManager conversationManager, BlogManager blogManager, ConversationManager conversationManager, BlogManager blogManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
BlogSharingManagerImpl blogSharingManager) { BlogSharingManagerImpl blogSharingManager,
CleanupManager cleanupManager) {
lifecycleManager.registerOpenDatabaseHook(blogSharingManager); lifecycleManager.registerOpenDatabaseHook(blogSharingManager);
contactManager.registerContactHook(blogSharingManager); contactManager.registerContactHook(blogSharingManager);
validationManager.registerIncomingMessageHook( validationManager.registerIncomingMessageHook(
@@ -91,6 +93,9 @@ public class SharingModule {
clientVersioningManager.registerClient(BlogManager.CLIENT_ID, clientVersioningManager.registerClient(BlogManager.CLIENT_ID,
BlogManager.MAJOR_VERSION, BlogManager.MINOR_VERSION, BlogManager.MAJOR_VERSION, BlogManager.MINOR_VERSION,
blogSharingManager.getShareableClientVersioningHook()); blogSharingManager.getShareableClientVersioningHook());
cleanupManager.registerCleanupHook(BlogSharingManager.CLIENT_ID,
BlogSharingManager.MAJOR_VERSION,
blogSharingManager);
return blogSharingManager; return blogSharingManager;
} }
@@ -134,7 +139,8 @@ public class SharingModule {
ValidationManager validationManager, ValidationManager validationManager,
ConversationManager conversationManager, ForumManager forumManager, ConversationManager conversationManager, ForumManager forumManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
ForumSharingManagerImpl forumSharingManager) { ForumSharingManagerImpl forumSharingManager,
CleanupManager cleanupManager) {
lifecycleManager.registerOpenDatabaseHook(forumSharingManager); lifecycleManager.registerOpenDatabaseHook(forumSharingManager);
contactManager.registerContactHook(forumSharingManager); contactManager.registerContactHook(forumSharingManager);
validationManager.registerIncomingMessageHook( validationManager.registerIncomingMessageHook(
@@ -150,6 +156,9 @@ public class SharingModule {
clientVersioningManager.registerClient(ForumManager.CLIENT_ID, clientVersioningManager.registerClient(ForumManager.CLIENT_ID,
ForumManager.MAJOR_VERSION, ForumManager.MINOR_VERSION, ForumManager.MAJOR_VERSION, ForumManager.MINOR_VERSION,
forumSharingManager.getShareableClientVersioningHook()); forumSharingManager.getShareableClientVersioningHook());
cleanupManager.registerCleanupHook(ForumSharingManager.CLIENT_ID,
ForumSharingManager.MAJOR_VERSION,
forumSharingManager);
return forumSharingManager; return forumSharingManager;
} }

View File

@@ -74,7 +74,8 @@ abstract class SharingValidator extends BdfMessageValidator {
} }
BdfDictionary meta = messageEncoder.encodeMetadata(INVITE, shareableId, BdfDictionary meta = messageEncoder.encodeMetadata(INVITE, shareableId,
m.getTimestamp(), false, false, false, false, false, timer); m.getTimestamp(), false, false, false, false, false, timer,
false);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -96,7 +97,7 @@ abstract class SharingValidator extends BdfMessageValidator {
BdfDictionary meta = messageEncoder.encodeMetadata(type, BdfDictionary meta = messageEncoder.encodeMetadata(type,
new GroupId(shareableId), m.getTimestamp(), false, false, new GroupId(shareableId), m.getTimestamp(), false, false,
false, false, false, NO_AUTO_DELETE_TIMER); false, false, false, NO_AUTO_DELETE_TIMER, false);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -123,7 +124,7 @@ abstract class SharingValidator extends BdfMessageValidator {
BdfDictionary meta = messageEncoder.encodeMetadata(type, BdfDictionary meta = messageEncoder.encodeMetadata(type,
new GroupId(shareableId), m.getTimestamp(), false, false, new GroupId(shareableId), m.getTimestamp(), false, false,
false, false, false, timer); false, false, false, timer, false);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {

View File

@@ -0,0 +1,278 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.client.BaseGroup;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.Shareable;
import org.briarproject.briar.api.sharing.SharingInvitationItem;
import org.briarproject.briar.api.sharing.SharingManager;
import org.briarproject.briar.autodelete.AbstractAutoDeleteTest;
import java.util.Collection;
import static org.briarproject.bramble.api.cleanup.CleanupManager.BATCH_DELAY_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.test.TestEventListener.assertEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public abstract class AbstractAutoDeleteIntegrationTest
extends AbstractAutoDeleteTest {
protected abstract SharingManager<? extends Shareable> getSharingManager0();
protected abstract SharingManager<? extends Shareable> getSharingManager1();
protected abstract Shareable getShareable();
protected abstract Collection<? extends Shareable> subscriptions0()
throws DbException;
protected abstract Collection<? extends Shareable> subscriptions1()
throws DbException;
protected abstract Class<? extends ConversationMessageReceivedEvent<? extends InvitationResponse>> getResponseReceivedEventClass();
protected void testAutoDeclinedSharing() throws Exception {
setAutoDeleteTimer(c0, contactId1From0, MIN_AUTO_DELETE_TIMER_MS);
// Send invitation
getSharingManager0().sendInvitation(
getShareable().getId(), contactId1From0, "This shareable!");
// The message should have been added to 0's view of the conversation
assertGroupCount(c0, contactId1From0, 1, 0);
forEachHeader(c0, contactId1From0, 1, h -> {
// The message should have the expected timer
assertEquals(MIN_AUTO_DELETE_TIMER_MS, h.getAutoDeleteTimer());
});
// Sync invitation message
sync0To1(1, true);
ack1To0(1);
waitForEvents(c0);
// The message should have been added to 1's view of the conversation
assertGroupCount(c1, contactId0From1, 1, 1);
forEachHeader(c1, contactId0From1, 1, h -> {
// The message should have the expected timer
assertEquals(MIN_AUTO_DELETE_TIMER_MS, h.getAutoDeleteTimer());
});
// Both peers should be using the new timer
assertEquals(MIN_AUTO_DELETE_TIMER_MS,
getAutoDeleteTimer(c0, contactId1From0));
assertEquals(MIN_AUTO_DELETE_TIMER_MS,
getAutoDeleteTimer(c1, contactId0From1));
// Before 0's timer elapses, both peers should still see the message
long timerLatency = MIN_AUTO_DELETE_TIMER_MS + BATCH_DELAY_MS;
c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
assertGroupCount(c0, contactId1From0, 1, 0);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 1);
assertEquals(1, getMessageHeaders(c1, contactId0From1).size());
// 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
ConversationMessagesDeletedEvent event =
assertEvent(c0, ConversationMessagesDeletedEvent.class,
() -> c0.getTimeTravel().addCurrentTimeMillis(1)
);
c1.getTimeTravel().addCurrentTimeMillis(1);
// assert that the proper event got broadcast
assertEquals(contactId1From0, event.getContactId());
assertGroupCount(c0, contactId1From0, 0, 0);
assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 1);
assertEquals(1, getMessageHeaders(c1, contactId0From1).size());
// 1 marks the message as read - this starts 1's timer
final MessageId messageId0 =
getMessageHeaders(c1, contactId0From1).get(0).getId();
markMessageRead(c1, contact0From1, messageId0);
assertGroupCount(c1, contactId0From1, 1, 0);
// Before 1's timer elapses, 1 should still see the message
c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
assertGroupCount(c0, contactId1From0, 0, 0);
assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 0);
assertEquals(1, getMessageHeaders(c1, contactId0From1).size());
// When 1's timer has elapsed, the message should be deleted from 1's
// view of the conversation and the invitation auto-declined
c0.getTimeTravel().addCurrentTimeMillis(1);
ConversationMessageReceivedEvent<? extends InvitationResponse> e =
assertEvent(c1, getResponseReceivedEventClass(),
() -> c1.getTimeTravel().addCurrentTimeMillis(1)
);
// assert that the proper event got broadcast
assertEquals(contactId0From1, e.getContactId());
assertGroupCount(c0, contactId1From0, 0, 0);
assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 0);
forEachHeader(c1, contactId0From1, 1, h -> {
// The only message is not the same as before, but declined response
assertNotEquals(messageId0, h.getId());
assertTrue(h instanceof InvitationResponse);
assertEquals(h.getId(), e.getMessageHeader().getId());
assertFalse(((InvitationResponse) h).wasAccepted());
assertTrue(((InvitationResponse) h).isAutoDecline());
// The auto-decline message should have the expected timer
assertEquals(MIN_AUTO_DELETE_TIMER_MS,
h.getAutoDeleteTimer());
});
// Sync the auto-decline message to 0
sync1To0(1, true);
// Sync the ack to 1 - this starts 1's timer
ack0To1(1);
waitForEvents(c1);
// 0 can invite 1 again
assertTrue(getSharingManager0()
.canBeShared(getShareable().getId(), contact1From0));
// Before 1's timer elapses, 1 should still see the auto-decline message
c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
assertGroupCount(c0, contactId1From0, 1, 1);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 0);
assertEquals(1, getMessageHeaders(c1, contactId0From1).size());
// When 1's timer has elapsed, the auto-decline message should be
// deleted from 1's view of the conversation
c0.getTimeTravel().addCurrentTimeMillis(1);
c1.getTimeTravel().addCurrentTimeMillis(1);
assertGroupCount(c0, contactId1From0, 1, 1);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 0, 0);
assertEquals(0, getMessageHeaders(c1, contactId0From1).size());
// 0 marks the message as read - this starts 0's timer
MessageId messageId1 =
getMessageHeaders(c0, contactId1From0).get(0).getId();
markMessageRead(c0, contact1From0, messageId1);
assertGroupCount(c0, contactId1From0, 1, 0);
// Before 0's timer elapses, 0 should still see the message
c0.getTimeTravel().addCurrentTimeMillis(timerLatency - 1);
assertGroupCount(c0, contactId1From0, 1, 0);
assertEquals(1, getMessageHeaders(c0, contactId1From0).size());
// When 0's timer has elapsed, the message should be deleted from 0's
// view of the conversation
c0.getTimeTravel().addCurrentTimeMillis(1);
assertGroupCount(c0, contactId1From0, 0, 0);
assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
// 0 can invite 1 again and really does invite
assertTrue(getSharingManager0()
.canBeShared(getShareable().getId(), contact1From0));
// Send invitation
getSharingManager0()
.sendInvitation(getShareable().getId(), contactId1From0,
"This shareable, please be quick!");
sync0To1(1, true);
assertGroupCount(c1, contactId0From1, 1, 1);
}
protected void testRespondAfterSenderDeletedInvitation() throws Exception {
setAutoDeleteTimer(c0, contactId1From0, MIN_AUTO_DELETE_TIMER_MS);
assertTrue(subscriptions0().contains(getShareable()));
assertFalse(subscriptions1().contains(getShareable()));
// what we expect after 1 accepts
int expectedSubscriptions1 = subscriptions1().size() + 1;
getSharingManager0().sendInvitation(
getShareable().getId(), contactId1From0, "This shareable!");
sync0To1(1, true);
// 0's timer starts when it gets the ACK of the invitation
ack1To0(1);
waitForEvents(c0);
assertGroupCount(c1, contactId0From1, 1, 1);
// 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
long timerLatency = MIN_AUTO_DELETE_TIMER_MS + BATCH_DELAY_MS;
c0.getTimeTravel().addCurrentTimeMillis(timerLatency);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency);
assertGroupCount(c0, contactId1From0, 0, 0);
assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 1, 1);
assertEquals(1, getMessageHeaders(c1, contactId0From1).size());
// 1 marks as read, timer starting
markMessageRead(c1, contact0From1,
getMessageHeaders(c1, contactId0From1).get(0).getId());
assertGroupCount(c1, contactId0From1, 1, 0);
// 1 accepts the invitation that 0 has already deleted
assertEquals(1, getSharingManager1().getInvitations().size());
SharingInvitationItem invitation =
getSharingManager1().getInvitations().iterator().next();
assertEquals(getShareable(), invitation.getShareable());
Contact c = contactManager1.getContact(contactId0From1);
if (getShareable() instanceof Blog) {
//noinspection unchecked
((SharingManager<Blog>) getSharingManager1()).respondToInvitation(
(Blog) getShareable(), c, true);
} else if (getShareable() instanceof Forum) {
//noinspection unchecked
((SharingManager<Forum>) getSharingManager1()).respondToInvitation(
(Forum) getShareable(), c, true);
} else {
fail();
}
// Sync the invitation response message to 0
sync1To0(1, true);
// 1's timer starts when it gets the ACK
ack0To1(1);
waitForEvents(c1);
assertGroupCount(c0, contactId1From0, 1, 1);
assertGroupCount(c1, contactId0From1, 2, 0);
// 0 marks as read, timer starting
markMessageRead(c0, contact1From0,
getMessageHeaders(c0, contactId1From0).get(0).getId());
assertGroupCount(c0, contactId1From0, 1, 0);
assertGroupCount(c1, contactId0From1, 2, 0);
// ensure accept message (invitation response) is linking to shareable
InvitationResponse acceptMessage = (InvitationResponse)
getMessageHeaders(c1, contactId0From1).get(1);
assertEquals(acceptMessage.getShareableId(),
((BaseGroup) getShareable()).getId());
// both peers delete all messages after their timers expire
c0.getTimeTravel().addCurrentTimeMillis(timerLatency);
c1.getTimeTravel().addCurrentTimeMillis(timerLatency);
assertGroupCount(c0, contactId1From0, 0, 0);
assertEquals(0, getMessageHeaders(c0, contactId1From0).size());
assertGroupCount(c1, contactId0From1, 0, 0);
assertEquals(0, getMessageHeaders(c1, contactId0From1).size());
// there are no invitations hanging around
assertEquals(0, getSharingManager0().getInvitations().size());
assertEquals(0, getSharingManager1().getInvitations().size());
assertEquals(expectedSubscriptions1, subscriptions1().size());
}
}

View File

@@ -0,0 +1,87 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.Shareable;
import org.briarproject.briar.api.sharing.SharingManager;
import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.junit.Before;
import org.junit.Test;
import java.util.Collection;
public class AutoDeleteBlogIntegrationTest
extends AbstractAutoDeleteIntegrationTest {
private SharingManager<Blog> sharingManager0;
private SharingManager<Blog> sharingManager1;
private Blog shareable;
private BlogManager manager0;
private BlogManager manager1;
private Class<BlogInvitationResponseReceivedEvent>
responseReceivedEventClass;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
manager0 = c0.getBlogManager();
manager1 = c1.getBlogManager();
// personalBlog(author0) is already shared with c1
shareable = manager0.getPersonalBlog(author2);
sharingManager0 = c0.getBlogSharingManager();
sharingManager1 = c1.getBlogSharingManager();
responseReceivedEventClass = BlogInvitationResponseReceivedEvent.class;
}
@Override
protected ConversationManager.ConversationClient getConversationClient(
BriarIntegrationTestComponent component) {
return component.getBlogSharingManager();
}
@Override
protected SharingManager<? extends Shareable> getSharingManager0() {
return sharingManager0;
}
@Override
protected SharingManager<? extends Shareable> getSharingManager1() {
return sharingManager1;
}
@Override
protected Shareable getShareable() {
return shareable;
}
@Override
protected Collection<Blog> subscriptions0() throws DbException {
return manager0.getBlogs();
}
@Override
protected Collection<Blog> subscriptions1() throws DbException {
return manager1.getBlogs();
}
@Override
protected Class<? extends ConversationMessageReceivedEvent<? extends InvitationResponse>> getResponseReceivedEventClass() {
return responseReceivedEventClass;
}
@Test
public void testAutoDeclinedBlogSharing() throws Exception {
testAutoDeclinedSharing();
}
@Test
public void testRespondAfterSenderDeletedBlogInvitation() throws Exception {
testRespondAfterSenderDeletedInvitation();
}
}

View File

@@ -0,0 +1,87 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.Shareable;
import org.briarproject.briar.api.sharing.SharingManager;
import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.junit.Before;
import org.junit.Test;
import java.util.Collection;
public class AutoDeleteForumIntegrationTest
extends AbstractAutoDeleteIntegrationTest {
private SharingManager<Forum> sharingManager0;
private SharingManager<Forum> sharingManager1;
protected Forum shareable;
private ForumManager manager0;
private ForumManager manager1;
protected Class<ForumInvitationResponseReceivedEvent>
responseReceivedEventClass;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
manager0 = c0.getForumManager();
manager1 = c1.getForumManager();
shareable = manager0.addForum("Test Forum");
sharingManager0 = c0.getForumSharingManager();
sharingManager1 = c1.getForumSharingManager();
responseReceivedEventClass = ForumInvitationResponseReceivedEvent.class;
}
@Override
protected ConversationManager.ConversationClient getConversationClient(
BriarIntegrationTestComponent component) {
return component.getForumSharingManager();
}
@Override
protected SharingManager<? extends Shareable> getSharingManager0() {
return sharingManager0;
}
@Override
protected SharingManager<? extends Shareable> getSharingManager1() {
return sharingManager1;
}
@Override
protected Shareable getShareable() {
return shareable;
}
@Override
protected Collection<Forum> subscriptions0() throws DbException {
return manager0.getForums();
}
@Override
protected Collection<Forum> subscriptions1() throws DbException {
return manager1.getForums();
}
@Override
protected Class<? extends ConversationMessageReceivedEvent<? extends InvitationResponse>> getResponseReceivedEventClass() {
return responseReceivedEventClass;
}
@Test
public void testAutoDeclinedForumSharing() throws Exception {
testAutoDeclinedSharing();
}
@Test
public void testRespondAfterSenderDeletedForumInvitation()
throws Exception {
testRespondAfterSenderDeletedInvitation();
}
}

View File

@@ -210,7 +210,7 @@ public abstract class SharingValidatorTest extends ValidatorTestCase {
void expectEncodeMetadata(MessageType type, long autoDeleteTimer) { void expectEncodeMetadata(MessageType type, long autoDeleteTimer) {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageEncoder).encodeMetadata(type, groupId, timestamp, oneOf(messageEncoder).encodeMetadata(type, groupId, timestamp,
false, false, false, false, false, autoDeleteTimer); false, false, false, false, false, autoDeleteTimer, false);
will(returnValue(meta)); will(returnValue(meta));
}}); }});
} }