mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Auto-decline/auto-delete Forum & Blog sharing invitations/responses
This commit is contained in:
@@ -148,6 +148,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
|
||||
localTimestamp, s.getLastLocalMessageId(), descriptor,
|
||||
text, 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 {
|
||||
m = messageEncoder.encodeInviteMessage(s.getContactGroupId(),
|
||||
localTimestamp, s.getLastLocalMessageId(), descriptor,
|
||||
@@ -216,6 +220,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
|
||||
s.getShareableId(), localTimestamp,
|
||||
s.getLastLocalMessageId(), 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 {
|
||||
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
|
||||
s.getShareableId(), localTimestamp,
|
||||
@@ -271,6 +279,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
|
||||
s.getShareableId(), localTimestamp,
|
||||
s.getLastLocalMessageId(), timer);
|
||||
sendMessage(txn, m, DECLINE, s.getShareableId(), true, timer);
|
||||
// Set the auto-delete timer duration on the local message
|
||||
if (timer != NO_AUTO_DELETE_TIMER) {
|
||||
db.setCleanupTimerDuration(txn, m.getId(), timer);
|
||||
}
|
||||
} else {
|
||||
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
|
||||
s.getShareableId(), localTimestamp,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
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.ContactGroupFactory;
|
||||
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.versioning.ClientVersioningManager;
|
||||
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.SessionId;
|
||||
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||
@@ -47,18 +49,20 @@ import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
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.ACCEPT;
|
||||
import static org.briarproject.briar.sharing.MessageType.DECLINE;
|
||||
import static org.briarproject.briar.sharing.MessageType.INVITE;
|
||||
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;
|
||||
|
||||
@NotNullByDefault
|
||||
abstract class SharingManagerImpl<S extends Shareable>
|
||||
extends ConversationClientImpl
|
||||
implements SharingManager<S>, OpenDatabaseHook, ContactHook,
|
||||
ClientVersioningHook {
|
||||
ClientVersioningHook, CleanupHook {
|
||||
|
||||
private final ClientVersioningManager clientVersioningManager;
|
||||
private final MessageParser<S> messageParser;
|
||||
@@ -133,6 +137,11 @@ abstract class SharingManagerImpl<S extends Shareable>
|
||||
BdfDictionary d) throws DbException, FormatException {
|
||||
// Parse the metadata
|
||||
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
|
||||
SessionId sessionId = getSessionId(meta.getShareableId());
|
||||
StoredSession ss = getSession(txn, m.getGroupId(), sessionId);
|
||||
@@ -293,7 +302,11 @@ abstract class SharingManagerImpl<S extends Shareable>
|
||||
@Override
|
||||
public void respondToInvitation(ContactId c, SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
db.transaction(false, txn -> respondToInvitation(txn, c, id, accept));
|
||||
}
|
||||
|
||||
private void respondToInvitation(Transaction txn, ContactId c,
|
||||
SessionId id, boolean accept) throws DbException {
|
||||
try {
|
||||
// Look up the session
|
||||
Contact contact = db.getContact(txn, c);
|
||||
@@ -308,11 +321,8 @@ abstract class SharingManagerImpl<S extends Shareable>
|
||||
else session = engine.onDeclineAction(txn, session);
|
||||
// Store the updated session
|
||||
storeSession(txn, ss.storageId, session);
|
||||
db.commitTransaction(txn);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,6 +583,7 @@ abstract class SharingManagerImpl<S extends Shareable>
|
||||
}, messageId -> false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DeletionResult deleteMessages(Transaction txn, ContactId c,
|
||||
Set<MessageId> messageIds) throws DbException {
|
||||
@@ -684,6 +695,56 @@ abstract class SharingManagerImpl<S extends Shareable>
|
||||
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 shareable we're invited to
|
||||
if (session.state == LOCAL_INVITED) {
|
||||
respondToInvitation(txn, c, entry.getKey(), false);
|
||||
}
|
||||
for (MessageId m : session.messages) {
|
||||
db.deleteMessage(txn, m);
|
||||
db.deleteMessageMetadata(txn, m);
|
||||
}
|
||||
}
|
||||
recalculateGroupCount(txn, g);
|
||||
|
||||
txn.attach(new ConversationMessagesDeletedEvent(c, messageIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MessageId> getMessageIds(Transaction txn, ContactId c)
|
||||
throws DbException {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.CleanupManager;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||
@@ -75,7 +76,8 @@ public class SharingModule {
|
||||
ValidationManager validationManager,
|
||||
ConversationManager conversationManager, BlogManager blogManager,
|
||||
ClientVersioningManager clientVersioningManager,
|
||||
BlogSharingManagerImpl blogSharingManager) {
|
||||
BlogSharingManagerImpl blogSharingManager,
|
||||
CleanupManager cleanupManager) {
|
||||
lifecycleManager.registerOpenDatabaseHook(blogSharingManager);
|
||||
contactManager.registerContactHook(blogSharingManager);
|
||||
validationManager.registerIncomingMessageHook(
|
||||
@@ -91,6 +93,9 @@ public class SharingModule {
|
||||
clientVersioningManager.registerClient(BlogManager.CLIENT_ID,
|
||||
BlogManager.MAJOR_VERSION, BlogManager.MINOR_VERSION,
|
||||
blogSharingManager.getShareableClientVersioningHook());
|
||||
cleanupManager.registerCleanupHook(BlogSharingManager.CLIENT_ID,
|
||||
BlogSharingManager.MAJOR_VERSION,
|
||||
blogSharingManager);
|
||||
return blogSharingManager;
|
||||
}
|
||||
|
||||
@@ -134,7 +139,8 @@ public class SharingModule {
|
||||
ValidationManager validationManager,
|
||||
ConversationManager conversationManager, ForumManager forumManager,
|
||||
ClientVersioningManager clientVersioningManager,
|
||||
ForumSharingManagerImpl forumSharingManager) {
|
||||
ForumSharingManagerImpl forumSharingManager,
|
||||
CleanupManager cleanupManager) {
|
||||
lifecycleManager.registerOpenDatabaseHook(forumSharingManager);
|
||||
contactManager.registerContactHook(forumSharingManager);
|
||||
validationManager.registerIncomingMessageHook(
|
||||
@@ -150,6 +156,9 @@ public class SharingModule {
|
||||
clientVersioningManager.registerClient(ForumManager.CLIENT_ID,
|
||||
ForumManager.MAJOR_VERSION, ForumManager.MINOR_VERSION,
|
||||
forumSharingManager.getShareableClientVersioningHook());
|
||||
cleanupManager.registerCleanupHook(ForumSharingManager.CLIENT_ID,
|
||||
ForumSharingManager.MAJOR_VERSION,
|
||||
forumSharingManager);
|
||||
return forumSharingManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
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.autodelete.AbstractAutoDeleteTest;
|
||||
|
||||
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.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public abstract class AbstractAutoDeleteIntegrationTest
|
||||
extends AbstractAutoDeleteTest {
|
||||
|
||||
protected SharingManager<?> sharingManager0;
|
||||
protected Shareable shareable;
|
||||
|
||||
protected void testAutoDeclinedSharing(SharingManager<?> sharingManager0,
|
||||
Shareable shareable) throws Exception {
|
||||
setAutoDeleteTimer(c0, contactId1From0, MIN_AUTO_DELETE_TIMER_MS);
|
||||
|
||||
assertGroupCount(c0, contactId1From0, 1, 0);
|
||||
|
||||
// Send invitation
|
||||
sharingManager0
|
||||
.sendInvitation(shareable.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
|
||||
c0.getTimeTravel().addCurrentTimeMillis(1);
|
||||
c1.getTimeTravel().addCurrentTimeMillis(1);
|
||||
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);
|
||||
c1.getTimeTravel().addCurrentTimeMillis(1);
|
||||
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);
|
||||
assertFalse(((InvitationResponse) h).wasAccepted());
|
||||
//TODO 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(sharingManager0
|
||||
.canBeShared(shareable.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(sharingManager0
|
||||
.canBeShared(shareable.getId(), contact1From0));
|
||||
// Send invitation
|
||||
sharingManager0
|
||||
.sendInvitation(shareable.getId(), contactId1From0,
|
||||
"This shareable, please be quick!");
|
||||
sync0To1(1, true);
|
||||
assertGroupCount(c1, contactId0From1, 1, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||
import org.briarproject.briar.test.BriarIntegrationTestComponent;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AutoDeleteBlogIntegrationTest
|
||||
extends AbstractAutoDeleteIntegrationTest {
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// personalBlog(author0) is already shared with c1
|
||||
shareable = c0.getBlogManager().getPersonalBlog(author2);
|
||||
sharingManager0 = c0.getBlogSharingManager();
|
||||
addContacts1And2();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConversationManager.ConversationClient getConversationClient(
|
||||
BriarIntegrationTestComponent component) {
|
||||
return component.getBlogSharingManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoDeclinedBlogSharing() throws Exception {
|
||||
testAutoDeclinedSharing(sharingManager0, shareable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||
import org.briarproject.briar.api.forum.ForumManager;
|
||||
import org.briarproject.briar.test.BriarIntegrationTestComponent;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AutoDeleteForumIntegrationTest
|
||||
extends AbstractAutoDeleteIntegrationTest {
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
ForumManager forumManager0 = c0.getForumManager();
|
||||
shareable = forumManager0.addForum("Test Forum");
|
||||
sharingManager0 = c0.getForumSharingManager();
|
||||
addContacts1And2();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConversationManager.ConversationClient getConversationClient(
|
||||
BriarIntegrationTestComponent component) {
|
||||
return component.getForumSharingManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoDeclinedForumSharing() throws Exception {
|
||||
testAutoDeclinedSharing(sharingManager0, shareable);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user