Merge branch '1832-use-conversation-timestamp' into '804-self-destructing-messages'

Use latest conversation timestamp for all invitation/introduction messages

See merge request briar/briar!1310
This commit is contained in:
akwizgran
2020-12-03 17:32:33 +00:00
40 changed files with 639 additions and 453 deletions

View File

@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
@@ -39,6 +40,7 @@ import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.DozeWatchdog; import org.briarproject.briar.api.android.DozeWatchdog;
import org.briarproject.briar.api.android.LockManager; import org.briarproject.briar.api.android.LockManager;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory; import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
@@ -169,6 +171,10 @@ public interface AndroidComponent
AndroidWakeLockManager wakeLockManager(); AndroidWakeLockManager wakeLockManager();
TransactionManager transactionManager();
AutoDeleteManager autoDeleteManager();
void inject(SignInReminderReceiver briarService); void inject(SignInReminderReceiver briarService);
void inject(BriarService briarService); void inject(BriarService briarService);

View File

@@ -753,18 +753,10 @@ public class ConversationActivity extends BriarActivity
List<AttachmentHeader> attachmentHeaders) { List<AttachmentHeader> attachmentHeaders) {
if (isNullOrEmpty(text) && attachmentHeaders.isEmpty()) if (isNullOrEmpty(text) && attachmentHeaders.isEmpty())
throw new AssertionError(); throw new AssertionError();
long timestamp = System.currentTimeMillis(); viewModel.sendMessage(text, attachmentHeaders);
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
viewModel.sendMessage(text, attachmentHeaders, timestamp);
textInputView.clearText(); textInputView.clearText();
} }
private long getMinTimestampForNewMessage() {
// Don't use an earlier timestamp than the newest message
ConversationItem item = adapter.getLastItem();
return item == null ? 0 : item.getTime() + 1;
}
private void onAddedPrivateMessage(@Nullable PrivateMessageHeader h) { private void onAddedPrivateMessage(@Nullable PrivateMessageHeader h) {
if (h == null) return; if (h == null) return;
addConversationItem(h.accept(visitor)); addConversationItem(h.accept(visitor));
@@ -969,13 +961,11 @@ public class ConversationActivity extends BriarActivity
adapter.notifyItemChanged(position, item); adapter.notifyItemChanged(position, item);
} }
runOnDbThread(() -> { runOnDbThread(() -> {
long timestamp = System.currentTimeMillis();
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
try { try {
switch (item.getRequestType()) { switch (item.getRequestType()) {
case INTRODUCTION: case INTRODUCTION:
respondToIntroductionRequest(item.getSessionId(), respondToIntroductionRequest(item.getSessionId(),
accept, timestamp); accept);
break; break;
case FORUM: case FORUM:
respondToForumRequest(item.getSessionId(), accept); respondToForumRequest(item.getSessionId(), accept);
@@ -1051,9 +1041,8 @@ public class ConversationActivity extends BriarActivity
@DatabaseExecutor @DatabaseExecutor
private void respondToIntroductionRequest(SessionId sessionId, private void respondToIntroductionRequest(SessionId sessionId,
boolean accept, long time) throws DbException { boolean accept) throws DbException {
introductionManager.respondToIntroduction(contactId, sessionId, time, introductionManager.respondToIntroduction(contactId, sessionId, accept);
accept);
} }
@DatabaseExecutor @DatabaseExecutor

View File

@@ -30,6 +30,7 @@ import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.messaging.AttachmentHeader; import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage; import org.briarproject.briar.api.messaging.PrivateMessage;
@@ -86,6 +87,7 @@ public class ConversationViewModel extends AndroidViewModel
private final AttachmentRetriever attachmentRetriever; private final AttachmentRetriever attachmentRetriever;
private final AttachmentCreator attachmentCreator; private final AttachmentCreator attachmentCreator;
private final AutoDeleteManager autoDeleteManager; private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
@Nullable @Nullable
private ContactId contactId = null; private ContactId contactId = null;
@@ -119,7 +121,8 @@ public class ConversationViewModel extends AndroidViewModel
PrivateMessageFactory privateMessageFactory, PrivateMessageFactory privateMessageFactory,
AttachmentRetriever attachmentRetriever, AttachmentRetriever attachmentRetriever,
AttachmentCreator attachmentCreator, AttachmentCreator attachmentCreator,
AutoDeleteManager autoDeleteManager) { AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager) {
super(application); super(application);
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.db = db; this.db = db;
@@ -131,6 +134,7 @@ public class ConversationViewModel extends AndroidViewModel
this.attachmentRetriever = attachmentRetriever; this.attachmentRetriever = attachmentRetriever;
this.attachmentCreator = attachmentCreator; this.attachmentCreator = attachmentCreator;
this.autoDeleteManager = autoDeleteManager; this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
messagingGroupId = Transformations messagingGroupId = Transformations
.map(contact, c -> messagingManager.getContactGroup(c).getId()); .map(contact, c -> messagingManager.getContactGroup(c).getId());
contactDeleted.setValue(false); contactDeleted.setValue(false);
@@ -213,14 +217,13 @@ public class ConversationViewModel extends AndroidViewModel
} }
@UiThread @UiThread
void sendMessage(@Nullable String text, void sendMessage(@Nullable String text, List<AttachmentHeader> headers) {
List<AttachmentHeader> headers, long timestamp) {
// messagingGroupId is loaded with the contact // messagingGroupId is loaded with the contact
observeForeverOnce(messagingGroupId, groupId -> { observeForeverOnce(messagingGroupId, groupId -> {
requireNonNull(groupId); requireNonNull(groupId);
observeForeverOnce(privateMessageFormat, format -> observeForeverOnce(privateMessageFormat, format ->
storeMessage(requireNonNull(contactId), groupId, text, storeMessage(requireNonNull(contactId), groupId, text,
headers, timestamp, format)); headers, format));
}); });
} }
@@ -282,8 +285,10 @@ public class ConversationViewModel extends AndroidViewModel
private PrivateMessage createMessage(Transaction txn, ContactId c, private PrivateMessage createMessage(Transaction txn, ContactId c,
GroupId groupId, @Nullable String text, GroupId groupId, @Nullable String text,
List<AttachmentHeader> headers, long timestamp, List<AttachmentHeader> headers, PrivateMessageFormat format)
PrivateMessageFormat format) throws DbException { throws DbException {
long timestamp =
conversationManager.getTimestampForOutgoingMessage(txn, c);
try { try {
if (format == TEXT_ONLY) { if (format == TEXT_ONLY) {
return privateMessageFactory.createLegacyPrivateMessage( return privateMessageFactory.createLegacyPrivateMessage(
@@ -304,13 +309,13 @@ public class ConversationViewModel extends AndroidViewModel
@UiThread @UiThread
private void storeMessage(ContactId c, GroupId groupId, private void storeMessage(ContactId c, GroupId groupId,
@Nullable String text, List<AttachmentHeader> headers, @Nullable String text, List<AttachmentHeader> headers,
long timestamp, PrivateMessageFormat format) { PrivateMessageFormat format) {
dbExecutor.execute(() -> { dbExecutor.execute(() -> {
try { try {
db.transaction(false, txn -> { db.transaction(false, txn -> {
long start = now(); long start = now();
PrivateMessage m = createMessage(txn, c, groupId, text, PrivateMessage m = createMessage(txn, c, groupId, text,
headers, timestamp, format); headers, format);
messagingManager.addLocalMessage(txn, m); messagingManager.addLocalMessage(txn, m);
logDuration(LOG, "Storing message", start); logDuration(LOG, "Storing message", start);
Message message = m.getMessage(); Message message = m.getMessage();

View File

@@ -211,8 +211,7 @@ public class IntroductionMessageFragment extends BaseFragment
introductionActivity.runOnDbThread(() -> { introductionActivity.runOnDbThread(() -> {
// actually make the introduction // actually make the introduction
try { try {
long timestamp = System.currentTimeMillis(); introductionManager.makeIntroduction(c1, c2, text);
introductionManager.makeIntroduction(c1, c2, text, timestamp);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
introductionError(); introductionError();

View File

@@ -7,6 +7,8 @@ import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
@@ -15,6 +17,8 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl; import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.privategroup.GroupMessage; import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroup;
@@ -35,6 +39,8 @@ import javax.inject.Inject;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable @Immutable
@@ -43,9 +49,12 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
implements CreateGroupController { implements CreateGroupController {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(CreateGroupControllerImpl.class.getName()); getLogger(CreateGroupControllerImpl.class.getName());
private final Executor cryptoExecutor; private final Executor cryptoExecutor;
private final TransactionManager db;
private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
private final ContactManager contactManager; private final ContactManager contactManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final PrivateGroupFactory groupFactory; private final PrivateGroupFactory groupFactory;
@@ -56,16 +65,26 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
private final Clock clock; private final Clock clock;
@Inject @Inject
CreateGroupControllerImpl(@DatabaseExecutor Executor dbExecutor, CreateGroupControllerImpl(
@DatabaseExecutor Executor dbExecutor,
@CryptoExecutor Executor cryptoExecutor, @CryptoExecutor Executor cryptoExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager, TransactionManager db,
IdentityManager identityManager, PrivateGroupFactory groupFactory, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
LifecycleManager lifecycleManager,
ContactManager contactManager,
IdentityManager identityManager,
PrivateGroupFactory groupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
PrivateGroupManager groupManager, PrivateGroupManager groupManager,
GroupInvitationFactory groupInvitationFactory, GroupInvitationFactory groupInvitationFactory,
GroupInvitationManager groupInvitationManager, Clock clock) { GroupInvitationManager groupInvitationManager,
Clock clock) {
super(dbExecutor, lifecycleManager, contactManager); super(dbExecutor, lifecycleManager, contactManager);
this.cryptoExecutor = cryptoExecutor; this.cryptoExecutor = cryptoExecutor;
this.db = db;
this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
this.contactManager = contactManager; this.contactManager = contactManager;
this.identityManager = identityManager; this.identityManager = identityManager;
this.groupFactory = groupFactory; this.groupFactory = groupFactory;
@@ -129,16 +148,14 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
ResultExceptionHandler<Void, DbException> handler) { ResultExceptionHandler<Void, DbException> handler) {
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
LocalAuthor localAuthor = identityManager.getLocalAuthor(); db.transaction(true, txn -> {
List<Contact> contacts = new ArrayList<>(); LocalAuthor localAuthor =
for (ContactId c : contactIds) { identityManager.getLocalAuthor(txn);
try { List<InvitationContext> contexts =
contacts.add(contactManager.getContact(c)); createInvitationContexts(txn, contactIds);
} catch (NoSuchContactException e) { txn.attach(() -> signInvitations(g, localAuthor, contexts,
// Continue text, handler));
} });
}
signInvitations(g, localAuthor, contacts, text, handler);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
handler.onException(e); handler.onException(e);
@@ -146,17 +163,31 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
}); });
} }
private List<InvitationContext> createInvitationContexts(Transaction txn,
Collection<ContactId> contactIds) throws DbException {
List<InvitationContext> contexts = new ArrayList<>();
for (ContactId c : contactIds) {
try {
Contact contact = contactManager.getContact(txn, c);
long timestamp = conversationManager
.getTimestampForOutgoingMessage(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
contexts.add(new InvitationContext(contact, timestamp, timer));
} catch (NoSuchContactException e) {
// Continue
}
}
return contexts;
}
private void signInvitations(GroupId g, LocalAuthor localAuthor, private void signInvitations(GroupId g, LocalAuthor localAuthor,
Collection<Contact> contacts, @Nullable String text, List<InvitationContext> contexts, @Nullable String text,
ResultExceptionHandler<Void, DbException> handler) { ResultExceptionHandler<Void, DbException> handler) {
cryptoExecutor.execute(() -> { cryptoExecutor.execute(() -> {
long timestamp = clock.currentTimeMillis(); for (InvitationContext ctx : contexts) {
List<InvitationContext> contexts = new ArrayList<>(); ctx.signature = groupInvitationFactory.signInvitation(
for (Contact c : contacts) { ctx.contact, g, ctx.timestamp,
byte[] signature = groupInvitationFactory.signInvitation(c, g, localAuthor.getPrivateKey());
timestamp, localAuthor.getPrivateKey());
contexts.add(new InvitationContext(c.getId(), timestamp,
signature));
} }
sendInvitations(g, contexts, text, handler); sendInvitations(g, contexts, text, handler);
}); });
@@ -167,16 +198,16 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
ResultExceptionHandler<Void, DbException> handler) { ResultExceptionHandler<Void, DbException> handler) {
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
for (InvitationContext context : contexts) { for (InvitationContext ctx : contexts) {
try { try {
groupInvitationManager.sendInvitation(g, groupInvitationManager.sendInvitation(g,
context.contactId, text, context.timestamp, ctx.contact.getId(), text, ctx.timestamp,
context.signature); requireNonNull(ctx.signature),
ctx.autoDeleteTimer);
} catch (NoSuchContactException e) { } catch (NoSuchContactException e) {
// Continue // Continue
} }
} }
//noinspection ConstantConditions
handler.onResult(null); handler.onResult(null);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
@@ -187,15 +218,16 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
private static class InvitationContext { private static class InvitationContext {
private final ContactId contactId; private final Contact contact;
private final long timestamp; private final long timestamp, autoDeleteTimer;
private final byte[] signature; @Nullable
private byte[] signature = null;
private InvitationContext(ContactId contactId, long timestamp, private InvitationContext(Contact contact, long timestamp,
byte[] signature) { long autoDeleteTimer) {
this.contactId = contactId; this.contact = contact;
this.timestamp = timestamp; this.timestamp = timestamp;
this.signature = signature; this.autoDeleteTimer = autoDeleteTimer;
} }
} }
} }

View File

@@ -10,11 +10,9 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
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.system.Clock;
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl; import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
import org.briarproject.briar.android.controller.handler.ExceptionHandler; import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.conversation.ConversationManager;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -25,6 +23,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable @Immutable
@@ -33,21 +32,16 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
implements ShareBlogController { implements ShareBlogController {
private final static Logger LOG = private final static Logger LOG =
Logger.getLogger(ShareBlogControllerImpl.class.getName()); getLogger(ShareBlogControllerImpl.class.getName());
private final ConversationManager conversationManager;
private final BlogSharingManager blogSharingManager; private final BlogSharingManager blogSharingManager;
private final Clock clock;
@Inject @Inject
ShareBlogControllerImpl(@DatabaseExecutor Executor dbExecutor, ShareBlogControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ConversationManager conversationManager, BlogSharingManager blogSharingManager) {
BlogSharingManager blogSharingManager, Clock clock) {
super(dbExecutor, lifecycleManager, contactManager); super(dbExecutor, lifecycleManager, contactManager);
this.conversationManager = conversationManager;
this.blogSharingManager = blogSharingManager; this.blogSharingManager = blogSharingManager;
this.clock = clock;
} }
@Override @Override
@@ -62,10 +56,7 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
try { try {
for (ContactId c : contacts) { for (ContactId c : contacts) {
try { try {
long time = Math.max(clock.currentTimeMillis(), blogSharingManager.sendInvitation(g, c, text);
conversationManager.getGroupCount(c)
.getLatestMsgTime() + 1);
blogSharingManager.sendInvitation(g, c, text, time);
} catch (NoSuchContactException | NoSuchGroupException e) { } catch (NoSuchContactException | NoSuchGroupException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }

View File

@@ -10,10 +10,8 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
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.system.Clock;
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl; import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
import org.briarproject.briar.android.controller.handler.ExceptionHandler; import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import java.util.Collection; import java.util.Collection;
@@ -25,6 +23,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable @Immutable
@@ -33,21 +32,16 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
implements ShareForumController { implements ShareForumController {
private final static Logger LOG = private final static Logger LOG =
Logger.getLogger(ShareForumControllerImpl.class.getName()); getLogger(ShareForumControllerImpl.class.getName());
private final ConversationManager conversationManager;
private final ForumSharingManager forumSharingManager; private final ForumSharingManager forumSharingManager;
private final Clock clock;
@Inject @Inject
ShareForumControllerImpl(@DatabaseExecutor Executor dbExecutor, ShareForumControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ConversationManager conversationManager, ForumSharingManager forumSharingManager) {
ForumSharingManager forumSharingManager, Clock clock) {
super(dbExecutor, lifecycleManager, contactManager); super(dbExecutor, lifecycleManager, contactManager);
this.conversationManager = conversationManager;
this.forumSharingManager = forumSharingManager; this.forumSharingManager = forumSharingManager;
this.clock = clock;
} }
@Override @Override
@@ -62,10 +56,7 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
try { try {
for (ContactId c : contacts) { for (ContactId c : contacts) {
try { try {
long time = Math.max(clock.currentTimeMillis(), forumSharingManager.sendInvitation(g, c, text);
conversationManager.getGroupCount(c)
.getLatestMsgTime() + 1);
forumSharingManager.sendInvitation(g, c, text, time);
} catch (NoSuchContactException | NoSuchGroupException e) { } catch (NoSuchContactException | NoSuchGroupException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }

View File

@@ -43,6 +43,18 @@ public interface ConversationManager {
*/ */
GroupCount getGroupCount(ContactId c) throws DbException; GroupCount getGroupCount(ContactId c) throws DbException;
/**
* Returns the unified group count for all private conversation messages.
*/
GroupCount getGroupCount(Transaction txn, ContactId c) throws DbException;
/**
* Returns a timestamp for an outgoing message, which is later than the
* timestamp of any message in the conversation with the given contact.
*/
long getTimestampForOutgoingMessage(Transaction txn, ContactId c)
throws DbException;
/** /**
* Deletes all messages exchanged with the given contact. * Deletes all messages exchanged with the given contact.
*/ */

View File

@@ -36,13 +36,13 @@ public interface IntroductionManager extends ConversationClient {
/** /**
* Sends two initial introduction messages. * Sends two initial introduction messages.
*/ */
void makeIntroduction(Contact c1, Contact c2, @Nullable String text, void makeIntroduction(Contact c1, Contact c2, @Nullable String text)
long timestamp) throws DbException; throws DbException;
/** /**
* Responds to an introduction. * Responds to an introduction.
*/ */
void respondToIntroduction(ContactId contactId, SessionId sessionId, void respondToIntroduction(ContactId contactId, SessionId sessionId,
long timestamp, boolean accept) throws DbException; boolean accept) throws DbException;
} }

View File

@@ -43,7 +43,8 @@ public interface GroupInvitationManager extends ConversationClient {
* pending. * pending.
*/ */
void sendInvitation(GroupId g, ContactId c, @Nullable String text, void sendInvitation(GroupId g, ContactId c, @Nullable String text,
long timestamp, byte[] signature) throws DbException; long timestamp, byte[] signature, long autoDeleteTimer)
throws DbException;
/** /**
* Responds to a pending private group invitation from the given contact. * Responds to a pending private group invitation from the given contact.

View File

@@ -21,7 +21,7 @@ public interface SharingManager<S extends Shareable>
* including optional text. * including optional text.
*/ */
void sendInvitation(GroupId shareableId, ContactId contactId, void sendInvitation(GroupId shareableId, ContactId contactId,
@Nullable String text, long timestamp) throws DbException; @Nullable String text) throws DbException;
/** /**
* Responds to a pending group invitation * Responds to a pending group invitation

View File

@@ -26,6 +26,7 @@ import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
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.ConversationManager;
import org.briarproject.briar.api.introduction.IntroductionResponse; import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent; import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
@@ -59,6 +60,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
protected final MessageEncoder messageEncoder; protected final MessageEncoder messageEncoder;
protected final ClientVersioningManager clientVersioningManager; protected final ClientVersioningManager clientVersioningManager;
protected final AutoDeleteManager autoDeleteManager; protected final AutoDeleteManager autoDeleteManager;
protected final ConversationManager conversationManager;
protected final Clock clock; protected final Clock clock;
AbstractProtocolEngine( AbstractProtocolEngine(
@@ -72,6 +74,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
@@ -83,6 +86,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
this.messageEncoder = messageEncoder; this.messageEncoder = messageEncoder;
this.clientVersioningManager = clientVersioningManager; this.clientVersioningManager = clientVersioningManager;
this.autoDeleteManager = autoDeleteManager; this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
this.clock = clock; this.clock = clock;
} }
@@ -227,14 +231,10 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
return !dependency.equals(lastRemoteMessageId); return !dependency.equals(lastRemoteMessageId);
} }
long getLocalTimestamp(long localTimestamp, long requestTimestamp) { long getTimestampForOutgoingMessage(Transaction txn, GroupId contactGroupId)
return Math.max( throws DbException {
clock.currentTimeMillis(), ContactId c = getContactId(txn, contactGroupId);
Math.max( return conversationManager.getTimestampForOutgoingMessage(txn, c);
localTimestamp,
requestTimestamp
) + 1
);
} }
private ContactId getContactId(Transaction txn, GroupId contactGroupId) private ContactId getContactId(Transaction txn, GroupId contactGroupId)

View File

@@ -31,6 +31,7 @@ import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.introduction.IntroductionRequest; import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent; import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent; import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent;
@@ -43,6 +44,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_AUTH; import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_AUTH;
@@ -75,15 +77,17 @@ class IntroduceeProtocolEngine
IdentityManager identityManager, IdentityManager identityManager,
MessageParser messageParser, MessageParser messageParser,
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
Clock clock,
IntroductionCrypto crypto, IntroductionCrypto crypto,
KeyManager keyManager, KeyManager keyManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
AutoDeleteManager autoDeleteManager) { AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) {
super(db, clientHelper, contactManager, contactGroupFactory, super(db, clientHelper, contactManager, contactGroupFactory,
messageTracker, identityManager, messageParser, messageEncoder, messageTracker, identityManager, messageParser, messageEncoder,
clientVersioningManager, autoDeleteManager, clock); clientVersioningManager, autoDeleteManager,
conversationManager, clock);
this.crypto = crypto; this.crypto = crypto;
this.keyManager = keyManager; this.keyManager = keyManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
@@ -91,18 +95,18 @@ class IntroduceeProtocolEngine
@Override @Override
public IntroduceeSession onRequestAction(Transaction txn, public IntroduceeSession onRequestAction(Transaction txn,
IntroduceeSession session, @Nullable String text, long timestamp) { IntroduceeSession session, @Nullable String text) {
throw new UnsupportedOperationException(); // Invalid in this role throw new UnsupportedOperationException(); // Invalid in this role
} }
@Override @Override
public IntroduceeSession onAcceptAction(Transaction txn, public IntroduceeSession onAcceptAction(Transaction txn,
IntroduceeSession session, long timestamp) throws DbException { IntroduceeSession session) throws DbException {
switch (session.getState()) { switch (session.getState()) {
case AWAIT_RESPONSES: case AWAIT_RESPONSES:
case REMOTE_DECLINED: case REMOTE_DECLINED:
case REMOTE_ACCEPTED: case REMOTE_ACCEPTED:
return onLocalAccept(txn, session, timestamp); return onLocalAccept(txn, session);
case START: case START:
case LOCAL_DECLINED: case LOCAL_DECLINED:
case LOCAL_ACCEPTED: case LOCAL_ACCEPTED:
@@ -116,12 +120,12 @@ class IntroduceeProtocolEngine
@Override @Override
public IntroduceeSession onDeclineAction(Transaction txn, public IntroduceeSession onDeclineAction(Transaction txn,
IntroduceeSession session, long timestamp) throws DbException { IntroduceeSession session) throws DbException {
switch (session.getState()) { switch (session.getState()) {
case AWAIT_RESPONSES: case AWAIT_RESPONSES:
case REMOTE_DECLINED: case REMOTE_DECLINED:
case REMOTE_ACCEPTED: case REMOTE_ACCEPTED:
return onLocalDecline(txn, session, timestamp); return onLocalDecline(txn, session);
case START: case START:
case LOCAL_DECLINED: case LOCAL_DECLINED:
case LOCAL_ACCEPTED: case LOCAL_ACCEPTED:
@@ -272,7 +276,7 @@ class IntroduceeProtocolEngine
} }
private IntroduceeSession onLocalAccept(Transaction txn, private IntroduceeSession onLocalAccept(Transaction txn,
IntroduceeSession s, long timestamp) throws DbException { IntroduceeSession s) throws DbException {
// Mark the request message unavailable to answer // Mark the request message unavailable to answer
markRequestsUnavailableToAnswer(txn, s); markRequestsUnavailableToAnswer(txn, s);
@@ -283,8 +287,8 @@ class IntroduceeProtocolEngine
Map<TransportId, TransportProperties> transportProperties = Map<TransportId, TransportProperties> transportProperties =
transportPropertyManager.getLocalProperties(txn); transportPropertyManager.getLocalProperties(txn);
// Send a ACCEPT message // Send an ACCEPT message
long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s)); long localTimestamp = getTimestampForVisibleMessage(txn, s);
Message sent = sendAcceptMessage(txn, s, localTimestamp, publicKey, Message sent = sendAcceptMessage(txn, s, localTimestamp, publicKey,
localTimestamp, transportProperties, true); localTimestamp, transportProperties, true);
// Track the message // Track the message
@@ -309,12 +313,12 @@ class IntroduceeProtocolEngine
} }
private IntroduceeSession onLocalDecline(Transaction txn, private IntroduceeSession onLocalDecline(Transaction txn,
IntroduceeSession s, long timestamp) throws DbException { IntroduceeSession s) throws DbException {
// Mark the request message unavailable to answer // Mark the request message unavailable to answer
markRequestsUnavailableToAnswer(txn, s); markRequestsUnavailableToAnswer(txn, s);
// Send a DECLINE message // Send a DECLINE message
long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s)); long localTimestamp = getTimestampForVisibleMessage(txn, s);
Message sent = sendDeclineMessage(txn, s, localTimestamp, true); Message sent = sendDeclineMessage(txn, s, localTimestamp, true);
// Track the message // Track the message
@@ -412,8 +416,8 @@ class IntroduceeProtocolEngine
return abort(txn, s); return abort(txn, s);
} }
if (s.getState() != AWAIT_AUTH) throw new AssertionError(); if (s.getState() != AWAIT_AUTH) throw new AssertionError();
Message sent = sendAuthMessage(txn, s, getLocalTimestamp(s), mac, long localTimestamp = getTimestampForInvisibleMessage(s);
signature); Message sent = sendAuthMessage(txn, s, localTimestamp, mac, signature);
return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, sent, masterKey, return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, sent, masterKey,
aliceMacKey, bobMacKey); aliceMacKey, bobMacKey);
} }
@@ -464,7 +468,8 @@ class IntroduceeProtocolEngine
// send ACTIVATE message with a MAC // send ACTIVATE message with a MAC
byte[] mac = crypto.activateMac(s); byte[] mac = crypto.activateMac(s);
Message sent = sendActivateMessage(txn, s, getLocalTimestamp(s), mac); long localTimestamp = getTimestampForInvisibleMessage(s);
Message sent = sendActivateMessage(txn, s, localTimestamp, mac);
// Move to AWAIT_ACTIVATE state and clear key material from session // Move to AWAIT_ACTIVATE state and clear key material from session
return IntroduceeSession.awaitActivate(s, m, sent, keys); return IntroduceeSession.awaitActivate(s, m, sent, keys);
@@ -515,7 +520,8 @@ class IntroduceeProtocolEngine
markRequestsUnavailableToAnswer(txn, s); markRequestsUnavailableToAnswer(txn, s);
// Send an ABORT message // Send an ABORT message
Message sent = sendAbortMessage(txn, s, getLocalTimestamp(s)); long localTimestamp = getTimestampForInvisibleMessage(s);
Message sent = sendAbortMessage(txn, s, localTimestamp);
// Broadcast abort event for testing // Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId())); txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -530,9 +536,34 @@ class IntroduceeProtocolEngine
return isInvalidDependency(s.getLastRemoteMessageId(), dependency); return isInvalidDependency(s.getLastRemoteMessageId(), dependency);
} }
private long getLocalTimestamp(IntroduceeSession s) { /**
return getLocalTimestamp(s.getLocalTimestamp(), * Returns a timestamp for a visible outgoing message. The timestamp is
s.getRequestTimestamp()); * later than the timestamp of any message sent or received so far in the
* conversation, and later than the {@link
* #getSessionTimestamp(IntroduceeSession) session timestamp}.
*/
private long getTimestampForVisibleMessage(Transaction txn,
IntroduceeSession s) throws DbException {
long conversationTimestamp =
getTimestampForOutgoingMessage(txn, s.getContactGroupId());
return max(conversationTimestamp, getSessionTimestamp(s) + 1);
}
/**
* Returns a timestamp for an invisible outgoing message. The timestamp is
* later than the {@link #getSessionTimestamp(IntroduceeSession) session
* timestamp}.
*/
private long getTimestampForInvisibleMessage(IntroduceeSession s) {
return max(clock.currentTimeMillis(), getSessionTimestamp(s) + 1);
}
/**
* Returns the latest timestamp of any message sent so far in the session,
* and any request message received so far in the session.
*/
private long getSessionTimestamp(IntroduceeSession s) {
return max(s.getLocalTimestamp(), s.getRequestTimestamp());
} }
private void addSessionId(Transaction txn, MessageId m, SessionId sessionId) private void addSessionId(Transaction txn, MessageId m, SessionId sessionId)

View File

@@ -17,6 +17,7 @@ import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent; import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
import org.briarproject.briar.introduction.IntroducerSession.Introducee; import org.briarproject.briar.introduction.IntroducerSession.Introducee;
@@ -24,6 +25,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.Math.max;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATES; import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATES;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_A; import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_A;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_B; import static org.briarproject.briar.introduction.IntroducerState.AWAIT_ACTIVATE_B;
@@ -54,19 +56,21 @@ class IntroducerProtocolEngine
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
super(db, clientHelper, contactManager, contactGroupFactory, super(db, clientHelper, contactManager, contactGroupFactory,
messageTracker, identityManager, messageParser, messageEncoder, messageTracker, identityManager, messageParser, messageEncoder,
clientVersioningManager, autoDeleteManager, clock); clientVersioningManager, autoDeleteManager,
conversationManager, clock);
} }
@Override @Override
public IntroducerSession onRequestAction(Transaction txn, public IntroducerSession onRequestAction(Transaction txn,
IntroducerSession s, @Nullable String text, long timestamp) IntroducerSession s, @Nullable String text)
throws DbException { throws DbException {
switch (s.getState()) { switch (s.getState()) {
case START: case START:
return onLocalRequest(txn, s, text, timestamp); return onLocalRequest(txn, s, text);
case AWAIT_RESPONSES: case AWAIT_RESPONSES:
case AWAIT_RESPONSE_A: case AWAIT_RESPONSE_A:
case AWAIT_RESPONSE_B: case AWAIT_RESPONSE_B:
@@ -86,37 +90,24 @@ class IntroducerProtocolEngine
@Override @Override
public IntroducerSession onAcceptAction(Transaction txn, public IntroducerSession onAcceptAction(Transaction txn,
IntroducerSession s, long timestamp) { IntroducerSession s) {
throw new UnsupportedOperationException(); // Invalid in this role throw new UnsupportedOperationException(); // Invalid in this role
} }
@Override @Override
public IntroducerSession onDeclineAction(Transaction txn, public IntroducerSession onDeclineAction(Transaction txn,
IntroducerSession s, long timestamp) { IntroducerSession s) {
throw new UnsupportedOperationException(); // Invalid in this role throw new UnsupportedOperationException(); // Invalid in this role
} }
IntroducerSession onIntroduceeRemoved(Transaction txn, IntroducerSession onIntroduceeRemoved(Transaction txn,
Introducee remainingIntroducee, IntroducerSession session) Introducee remainingIntroducee, IntroducerSession session)
throws DbException { throws DbException {
// abort session // abort session with remaining introducee
IntroducerSession s = abort(txn, session); IntroducerSession s = abort(txn, session, remainingIntroducee);
// reset information for introducee that was removed
Introducee introduceeA, introduceeB;
if (remainingIntroducee.author.equals(s.getIntroduceeA().author)) {
introduceeA = s.getIntroduceeA();
introduceeB =
new Introducee(s.getSessionId(), s.getIntroduceeB().groupId,
s.getIntroduceeB().author);
} else if (remainingIntroducee.author
.equals(s.getIntroduceeB().author)) {
introduceeA =
new Introducee(s.getSessionId(), s.getIntroduceeA().groupId,
s.getIntroduceeA().author);
introduceeB = s.getIntroduceeB();
} else throw new DbException();
return new IntroducerSession(s.getSessionId(), s.getState(), return new IntroducerSession(s.getSessionId(), s.getState(),
s.getRequestTimestamp(), introduceeA, introduceeB); s.getRequestTimestamp(), s.getIntroduceeA(),
s.getIntroduceeB());
} }
@Override @Override
@@ -226,13 +217,13 @@ class IntroducerProtocolEngine
} }
private IntroducerSession onLocalRequest(Transaction txn, private IntroducerSession onLocalRequest(Transaction txn,
IntroducerSession s, @Nullable String text, long timestamp) IntroducerSession s, @Nullable String text) throws DbException {
throws DbException {
// Send REQUEST messages // Send REQUEST messages
long maxIntroduceeTimestamp = long timestampA =
Math.max(getLocalTimestamp(s, s.getIntroduceeA()), getTimestampForVisibleMessage(txn, s, s.getIntroduceeA());
getLocalTimestamp(s, s.getIntroduceeB())); long timestampB =
long localTimestamp = Math.max(timestamp, maxIntroduceeTimestamp); getTimestampForVisibleMessage(txn, s, s.getIntroduceeB());
long localTimestamp = max(timestampA, timestampB);
Message sentA = sendRequestMessage(txn, s.getIntroduceeA(), Message sentA = sendRequestMessage(txn, s.getIntroduceeA(),
localTimestamp, s.getIntroduceeB().author, text); localTimestamp, s.getIntroduceeB().author, text);
Message sentB = sendRequestMessage(txn, s.getIntroduceeB(), Message sentB = sendRequestMessage(txn, s.getIntroduceeB(),
@@ -272,11 +263,11 @@ class IntroducerProtocolEngine
// Forward ACCEPT message // Forward ACCEPT message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i); // The forwarded message will not be visible to the introducee
Message sent = long localTimestamp = getTimestampForInvisibleMessage(s, i);
sendAcceptMessage(txn, i, timestamp, m.getEphemeralPublicKey(), Message sent = sendAcceptMessage(txn, i, localTimestamp,
m.getAcceptTimestamp(), m.getTransportProperties(), m.getEphemeralPublicKey(), m.getAcceptTimestamp(),
false); m.getTransportProperties(), false);
// Create the next state // Create the next state
IntroducerState state = AWAIT_AUTHS; IntroducerState state = AWAIT_AUTHS;
@@ -333,7 +324,9 @@ class IntroducerProtocolEngine
// Forward ACCEPT message // Forward ACCEPT message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
Message sent = sendAcceptMessage(txn, i, getLocalTimestamp(s, i), // The forwarded message will not be visible to the introducee
long localTimestamp = getTimestampForInvisibleMessage(s, i);
Message sent = sendAcceptMessage(txn, i, localTimestamp,
m.getEphemeralPublicKey(), m.getAcceptTimestamp(), m.getEphemeralPublicKey(), m.getAcceptTimestamp(),
m.getTransportProperties(), false); m.getTransportProperties(), false);
@@ -384,8 +377,9 @@ class IntroducerProtocolEngine
// Forward DECLINE message // Forward DECLINE message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i); // The forwarded message will be visible to the introducee
Message sent = sendDeclineMessage(txn, i, timestamp, false); long localTimestamp = getTimestampForVisibleMessage(txn, s, i);
Message sent = sendDeclineMessage(txn, i, localTimestamp, false);
// Create the next state // Create the next state
IntroducerState state = START; IntroducerState state = START;
@@ -436,8 +430,9 @@ class IntroducerProtocolEngine
// Forward DECLINE message // Forward DECLINE message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i); // The forwarded message will be visible to the introducee
Message sent = sendDeclineMessage(txn, i, timestamp, false); long localTimestamp = getTimestampForVisibleMessage(txn, s, i);
Message sent = sendDeclineMessage(txn, i, localTimestamp, false);
Introducee introduceeA, introduceeB; Introducee introduceeA, introduceeB;
Author sender, other; Author sender, other;
@@ -477,8 +472,8 @@ class IntroducerProtocolEngine
// Forward AUTH message // Forward AUTH message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i); long localTimestamp = getTimestampForInvisibleMessage(s, i);
Message sent = sendAuthMessage(txn, i, timestamp, m.getMac(), Message sent = sendAuthMessage(txn, i, localTimestamp, m.getMac(),
m.getSignature()); m.getSignature());
// Move to the next state // Move to the next state
@@ -513,8 +508,8 @@ class IntroducerProtocolEngine
// Forward ACTIVATE message // Forward ACTIVATE message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i); long localTimestamp = getTimestampForInvisibleMessage(s, i);
Message sent = sendActivateMessage(txn, i, timestamp, m.getMac()); Message sent = sendActivateMessage(txn, i, localTimestamp, m.getMac());
// Move to the next state // Move to the next state
IntroducerState state = START; IntroducerState state = START;
@@ -536,8 +531,8 @@ class IntroducerProtocolEngine
IntroducerSession s, AbortMessage m) throws DbException { IntroducerSession s, AbortMessage m) throws DbException {
// Forward ABORT message // Forward ABORT message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i); long localTimestamp = getTimestampForInvisibleMessage(s, i);
Message sent = sendAbortMessage(txn, i, timestamp); Message sent = sendAbortMessage(txn, i, localTimestamp);
// Broadcast abort event for testing // Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId())); txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -555,15 +550,45 @@ class IntroducerProtocolEngine
s.getRequestTimestamp(), introduceeA, introduceeB); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession abort(Transaction txn, private IntroducerSession abort(Transaction txn, IntroducerSession s,
IntroducerSession s) throws DbException { Introducee remainingIntroducee) throws DbException {
// Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
// Send an ABORT message to the remaining introducee
long localTimestamp =
getTimestampForInvisibleMessage(s, remainingIntroducee);
Message sent =
sendAbortMessage(txn, remainingIntroducee, localTimestamp);
// Reset the session back to initial state
Introducee introduceeA = s.getIntroduceeA();
Introducee introduceeB = s.getIntroduceeB();
if (remainingIntroducee.author.equals(introduceeA.author)) {
introduceeA = new Introducee(introduceeA, sent);
introduceeB = new Introducee(s.getSessionId(), introduceeB.groupId,
introduceeB.author);
} else if (remainingIntroducee.author.equals(introduceeB.author)) {
introduceeA = new Introducee(s.getSessionId(), introduceeA.groupId,
introduceeA.author);
introduceeB = new Introducee(introduceeB, sent);
} else {
throw new DbException();
}
return new IntroducerSession(s.getSessionId(), START,
s.getRequestTimestamp(), introduceeA, introduceeB);
}
private IntroducerSession abort(Transaction txn, IntroducerSession s)
throws DbException {
// Broadcast abort event for testing // Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId())); txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
// Send an ABORT message to both introducees // Send an ABORT message to both introducees
long timestampA = getLocalTimestamp(s, s.getIntroduceeA()); long timestampA =
getTimestampForInvisibleMessage(s, s.getIntroduceeA());
Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA); Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA);
long timestampB = getLocalTimestamp(s, s.getIntroduceeB()); long timestampB =
getTimestampForInvisibleMessage(s, s.getIntroduceeB());
Message sentB = sendAbortMessage(txn, s.getIntroduceeB(), timestampB); Message sentB = sendAbortMessage(txn, s.getIntroduceeB(), timestampB);
// Reset the session back to initial state // Reset the session back to initial state
Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA); Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA);
@@ -593,9 +618,33 @@ class IntroducerProtocolEngine
return isInvalidDependency(expected, dependency); return isInvalidDependency(expected, dependency);
} }
private long getLocalTimestamp(IntroducerSession s, PeerSession p) { /**
return getLocalTimestamp(p.getLocalTimestamp(), * Returns a timestamp for a visible outgoing message. The timestamp is
s.getRequestTimestamp()); * later than the timestamp of any message sent or received so far in the
* conversation, and later than the {@link
* #getSessionTimestamp(IntroducerSession, PeerSession) session timestamp}.
*/
private long getTimestampForVisibleMessage(Transaction txn,
IntroducerSession s, PeerSession p) throws DbException {
long conversationTimestamp =
getTimestampForOutgoingMessage(txn, p.getContactGroupId());
return max(conversationTimestamp, getSessionTimestamp(s, p) + 1);
} }
/**
* Returns a timestamp for an invisible outgoing message. The timestamp is
* later than the {@link #getSessionTimestamp(IntroducerSession, PeerSession)
* session timestamp}.
*/
private long getTimestampForInvisibleMessage(IntroducerSession s,
PeerSession p) {
return max(clock.currentTimeMillis(), getSessionTimestamp(s, p) + 1);
}
/**
* Returns the latest timestamp of any message sent so far in the session.
*/
private long getSessionTimestamp(IntroducerSession s, PeerSession p) {
return max(p.getLocalTimestamp(), s.getRequestTimestamp());
}
} }

View File

@@ -311,8 +311,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
} }
@Override @Override
public void makeIntroduction(Contact c1, Contact c2, @Nullable String text, public void makeIntroduction(Contact c1, Contact c2, @Nullable String text)
long timestamp) throws DbException { throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// Look up the session, if there is one // Look up the session, if there is one
@@ -344,8 +344,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
storageId = ss.storageId; storageId = ss.storageId;
} }
// Handle the request action // Handle the request action
session = introducerEngine session = introducerEngine.onRequestAction(txn, session, text);
.onRequestAction(txn, session, text, timestamp);
// Store the updated session // Store the updated session
storeSession(txn, storageId, session); storeSession(txn, storageId, session);
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -358,7 +357,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
@Override @Override
public void respondToIntroduction(ContactId contactId, SessionId sessionId, public void respondToIntroduction(ContactId contactId, SessionId sessionId,
long timestamp, boolean accept) throws DbException { boolean accept) throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// Look up the session // Look up the session
@@ -376,11 +375,9 @@ class IntroductionManagerImpl extends ConversationClientImpl
.parseIntroduceeSession(contactGroupId, ss.bdfSession); .parseIntroduceeSession(contactGroupId, ss.bdfSession);
// Handle the join or leave action // Handle the join or leave action
if (accept) { if (accept) {
session = introduceeEngine session = introduceeEngine.onAcceptAction(txn, session);
.onAcceptAction(txn, session, timestamp);
} else { } else {
session = introduceeEngine session = introduceeEngine.onDeclineAction(txn, session);
.onDeclineAction(txn, session, timestamp);
} }
// Store the updated session // Store the updated session
storeSession(txn, ss.storageId, session); storeSession(txn, ss.storageId, session);

View File

@@ -8,16 +8,14 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
interface ProtocolEngine<S extends Session> { interface ProtocolEngine<S extends Session<?>> {
S onRequestAction(Transaction txn, S session, @Nullable String text, S onRequestAction(Transaction txn, S session, @Nullable String text)
long timestamp) throws DbException;
S onAcceptAction(Transaction txn, S session, long timestamp)
throws DbException; throws DbException;
S onDeclineAction(Transaction txn, S session, long timestamp) S onAcceptAction(Transaction txn, S session) throws DbException;
throws DbException;
S onDeclineAction(Transaction txn, S session) throws DbException;
S onRequestMessage(Transaction txn, S session, RequestMessage m) S onRequestMessage(Transaction txn, S session, RequestMessage m)
throws DbException, FormatException; throws DbException, FormatException;

View File

@@ -6,6 +6,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.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.conversation.ConversationManager; import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.conversation.ConversationMessageHeader; import org.briarproject.briar.api.conversation.ConversationMessageHeader;
@@ -20,16 +21,20 @@ import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.Math.max;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
class ConversationManagerImpl implements ConversationManager { class ConversationManagerImpl implements ConversationManager {
private final DatabaseComponent db; private final DatabaseComponent db;
private final Clock clock;
private final Set<ConversationClient> clients; private final Set<ConversationClient> clients;
@Inject @Inject
ConversationManagerImpl(DatabaseComponent db) { ConversationManagerImpl(DatabaseComponent db, Clock clock) {
this.db = db; this.db = db;
this.clock = clock;
clients = new CopyOnWriteArraySet<>(); clients = new CopyOnWriteArraySet<>();
} }
@@ -57,24 +62,33 @@ class ConversationManagerImpl implements ConversationManager {
@Override @Override
public GroupCount getGroupCount(ContactId contactId) throws DbException { public GroupCount getGroupCount(ContactId contactId) throws DbException {
return db.transactionWithResult(true, txn ->
getGroupCount(txn, contactId));
}
@Override
public GroupCount getGroupCount(Transaction txn, ContactId contactId)
throws DbException {
int msgCount = 0, unreadCount = 0; int msgCount = 0, unreadCount = 0;
long latestTime = 0; long latestTime = 0;
Transaction txn = db.startTransaction(true); for (ConversationClient client : clients) {
try { GroupCount count = client.getGroupCount(txn, contactId);
for (ConversationClient client : clients) { msgCount += count.getMsgCount();
GroupCount count = client.getGroupCount(txn, contactId); unreadCount += count.getUnreadCount();
msgCount += count.getMsgCount(); if (count.getLatestMsgTime() > latestTime)
unreadCount += count.getUnreadCount(); latestTime = count.getLatestMsgTime();
if (count.getLatestMsgTime() > latestTime)
latestTime = count.getLatestMsgTime();
}
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
} }
return new GroupCount(msgCount, unreadCount, latestTime); return new GroupCount(msgCount, unreadCount, latestTime);
} }
@Override
public long getTimestampForOutgoingMessage(Transaction txn, ContactId c)
throws DbException {
long now = clock.currentTimeMillis();
GroupCount gc = getGroupCount(txn, c);
return max(now, gc.getLatestMsgTime() + 1);
}
@Override @Override
public DeletionResult deleteAllMessages(ContactId c) throws DbException { public DeletionResult deleteAllMessages(ContactId c) throws DbException {
return db.transactionWithResult(false, txn -> { return db.transactionWithResult(false, txn -> {
@@ -87,8 +101,8 @@ class ConversationManagerImpl implements ConversationManager {
} }
@Override @Override
public DeletionResult deleteMessages(ContactId c, Collection<MessageId> toDelete) public DeletionResult deleteMessages(ContactId c,
throws DbException { Collection<MessageId> toDelete) throws DbException {
return db.transactionWithResult(false, txn -> { return db.transactionWithResult(false, txn -> {
DeletionResult result = new DeletionResult(); DeletionResult result = new DeletionResult();
for (ConversationClient client : clients) { for (ConversationClient client : clients) {

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.privategroup.GroupMessage; import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroup;
@@ -31,6 +32,7 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static java.lang.Math.max;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT; import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT;
import static org.briarproject.briar.privategroup.invitation.MessageType.INVITE; import static org.briarproject.briar.privategroup.invitation.MessageType.INVITE;
@@ -54,6 +56,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
private final MessageParser messageParser; private final MessageParser messageParser;
private final MessageEncoder messageEncoder; private final MessageEncoder messageEncoder;
private final AutoDeleteManager autoDeleteManager; private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
private final Clock clock; private final Clock clock;
AbstractProtocolEngine( AbstractProtocolEngine(
@@ -68,6 +71,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
@@ -80,6 +84,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
this.messageEncoder = messageEncoder; this.messageEncoder = messageEncoder;
this.messageTracker = messageTracker; this.messageTracker = messageTracker;
this.autoDeleteManager = autoDeleteManager; this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
this.clock = clock; this.clock = clock;
} }
@@ -110,8 +115,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
} }
Message sendInviteMessage(Transaction txn, S s, Message sendInviteMessage(Transaction txn, S s,
@Nullable String text, long timestamp, byte[] signature) @Nullable String text, long timestamp, byte[] signature,
throws DbException { long timer) throws DbException {
Group g = db.getGroup(txn, s.getPrivateGroupId()); Group g = db.getGroup(txn, s.getPrivateGroupId());
PrivateGroup privateGroup; PrivateGroup privateGroup;
try { try {
@@ -122,7 +127,6 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m; Message m;
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
m = messageEncoder.encodeInviteMessage(s.getContactGroupId(), m = messageEncoder.encodeInviteMessage(s.getContactGroupId(),
privateGroup.getId(), timestamp, privateGroup.getName(), privateGroup.getId(), timestamp, privateGroup.getName(),
privateGroup.getCreator(), privateGroup.getSalt(), text, privateGroup.getCreator(), privateGroup.getSalt(), text,
@@ -142,6 +146,9 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message sendJoinMessage(Transaction txn, S s, boolean visibleInUi) Message sendJoinMessage(Transaction txn, S s, boolean visibleInUi)
throws DbException { throws DbException {
Message m; Message m;
long localTimestamp = visibleInUi
? getTimestampForVisibleMessage(txn, 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 manually accepting an invitation
@@ -149,13 +156,13 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
? autoDeleteManager.getAutoDeleteTimer(txn, c) ? autoDeleteManager.getAutoDeleteTimer(txn, c)
: NO_AUTO_DELETE_TIMER; : NO_AUTO_DELETE_TIMER;
m = messageEncoder.encodeJoinMessage(s.getContactGroupId(), m = messageEncoder.encodeJoinMessage(s.getContactGroupId(),
s.getPrivateGroupId(), getLocalTimestamp(s), s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);
sendMessage(txn, m, JOIN, s.getPrivateGroupId(), visibleInUi, sendMessage(txn, m, JOIN, s.getPrivateGroupId(), visibleInUi,
timer); timer);
} else { } else {
m = messageEncoder.encodeJoinMessage(s.getContactGroupId(), m = messageEncoder.encodeJoinMessage(s.getContactGroupId(),
s.getPrivateGroupId(), getLocalTimestamp(s), s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId()); s.getLastLocalMessageId());
sendMessage(txn, m, JOIN, s.getPrivateGroupId(), visibleInUi, sendMessage(txn, m, JOIN, s.getPrivateGroupId(), visibleInUi,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
@@ -166,6 +173,9 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message sendLeaveMessage(Transaction txn, S s, boolean visibleInUi) Message sendLeaveMessage(Transaction txn, S s, boolean visibleInUi)
throws DbException { throws DbException {
Message m; Message m;
long localTimestamp = visibleInUi
? getTimestampForVisibleMessage(txn, 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 manually accepting an invitation
@@ -173,13 +183,13 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
? autoDeleteManager.getAutoDeleteTimer(txn, c) ? autoDeleteManager.getAutoDeleteTimer(txn, c)
: NO_AUTO_DELETE_TIMER; : NO_AUTO_DELETE_TIMER;
m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(), m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(),
s.getPrivateGroupId(), getLocalTimestamp(s), 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);
} else { } else {
m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(), m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(),
s.getPrivateGroupId(), getLocalTimestamp(s), s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId()); s.getLastLocalMessageId());
sendMessage(txn, m, LEAVE, s.getPrivateGroupId(), visibleInUi, sendMessage(txn, m, LEAVE, s.getPrivateGroupId(), visibleInUi,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
@@ -190,7 +200,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message sendAbortMessage(Transaction txn, S session) throws DbException { Message sendAbortMessage(Transaction txn, S session) throws DbException {
Message m = messageEncoder.encodeAbortMessage( Message m = messageEncoder.encodeAbortMessage(
session.getContactGroupId(), session.getPrivateGroupId(), session.getContactGroupId(), session.getPrivateGroupId(),
getLocalTimestamp(session)); getTimestampForInvisibleMessage(session));
sendMessage(txn, m, ABORT, session.getPrivateGroupId(), false, sendMessage(txn, m, ABORT, session.getPrivateGroupId(), false,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
return m; return m;
@@ -246,7 +256,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup( PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
invite.getGroupName(), invite.getCreator(), invite.getSalt()); invite.getGroupName(), invite.getCreator(), invite.getSalt());
long timestamp = long timestamp =
Math.max(clock.currentTimeMillis(), invite.getTimestamp() + 1); max(clock.currentTimeMillis(), invite.getTimestamp() + 1);
// TODO: Create the join message on the crypto executor // TODO: Create the join message on the crypto executor
LocalAuthor member = identityManager.getLocalAuthor(txn); LocalAuthor member = identityManager.getLocalAuthor(txn);
GroupMessage joinMessage = groupMessageFactory.createJoinMessage( GroupMessage joinMessage = groupMessageFactory.createJoinMessage(
@@ -256,10 +266,34 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
.addPrivateGroup(txn, privateGroup, joinMessage, false); .addPrivateGroup(txn, privateGroup, joinMessage, false);
} }
long getLocalTimestamp(S session) { /**
return Math.max(clock.currentTimeMillis(), * Returns a timestamp for a visible outgoing message. The timestamp is
Math.max(session.getLocalTimestamp(), * later than the timestamp of any message sent or received so far in the
session.getInviteTimestamp()) + 1); * conversation, and later than the {@link #getSessionTimestamp(Session)
* session timestamp}.
*/
long getTimestampForVisibleMessage(Transaction txn, S s)
throws DbException {
ContactId c = getContactId(txn, s.getContactGroupId());
long conversationTimestamp =
conversationManager.getTimestampForOutgoingMessage(txn, c);
return max(conversationTimestamp, getSessionTimestamp(s) + 1);
}
/**
* Returns a timestamp for an invisible outgoing message. The timestamp is
* later than the {@link #getSessionTimestamp(Session) session timestamp}.
*/
long getTimestampForInvisibleMessage(S s) {
return max(clock.currentTimeMillis(), getSessionTimestamp(s) + 1);
}
/**
* Returns the latest timestamp of any message sent so far in the session,
* and any invite message sent or received so far in the session.
*/
private long getSessionTimestamp(S s) {
return max(s.getLocalTimestamp(), s.getInviteTimestamp());
} }
private void sendMessage(Transaction txn, Message m, MessageType type, private void sendMessage(Transaction txn, Message m, MessageType type,

View File

@@ -15,6 +15,7 @@ import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory; import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -24,6 +25,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRespons
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static java.lang.Math.max;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
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.privategroup.invitation.CreatorState.DISSOLVED; import static org.briarproject.briar.privategroup.invitation.CreatorState.DISSOLVED;
@@ -49,20 +51,22 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
super(db, clientHelper, clientVersioningManager, privateGroupManager, super(db, clientHelper, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, messageParser, messageEncoder, messageTracker,
autoDeleteManager, clock); autoDeleteManager, conversationManager, clock);
} }
@Override @Override
public CreatorSession onInviteAction(Transaction txn, CreatorSession s, public CreatorSession onInviteAction(Transaction txn, CreatorSession s,
@Nullable String text, long timestamp, byte[] signature) @Nullable String text, long timestamp, byte[] signature,
throws DbException { long autoDeleteTimer) throws DbException {
switch (s.getState()) { switch (s.getState()) {
case START: case START:
return onLocalInvite(txn, s, text, timestamp, signature); return onLocalInvite(txn, s, text, timestamp, signature,
autoDeleteTimer);
case INVITED: case INVITED:
case JOINED: case JOINED:
case LEFT: case LEFT:
@@ -152,14 +156,16 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
} }
private CreatorSession onLocalInvite(Transaction txn, CreatorSession s, private CreatorSession onLocalInvite(Transaction txn, CreatorSession s,
@Nullable String text, long timestamp, byte[] signature) @Nullable String text, long timestamp, byte[] signature,
throws DbException { long autoDeleteTimer) throws DbException {
// Send an INVITE message // Send an INVITE message
Message sent = sendInviteMessage(txn, s, text, timestamp, signature); Message sent = sendInviteMessage(txn, s, text, timestamp, signature,
autoDeleteTimer);
// Track the message // Track the message
messageTracker.trackOutgoingMessage(txn, sent); messageTracker.trackOutgoingMessage(txn, sent);
// Move to the INVITED state // Move to the INVITED state
long localTimestamp = Math.max(timestamp, getLocalTimestamp(s)); long localTimestamp =
max(timestamp, getTimestampForVisibleMessage(txn, s));
return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(), return new CreatorSession(s.getContactGroupId(), s.getPrivateGroupId(),
sent.getId(), s.getLastRemoteMessageId(), localTimestamp, sent.getId(), s.getLastRemoteMessageId(), localTimestamp,
timestamp, INVITED); timestamp, INVITED);

View File

@@ -257,8 +257,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
@Override @Override
public void sendInvitation(GroupId privateGroupId, ContactId c, public void sendInvitation(GroupId privateGroupId, ContactId c,
@Nullable String text, long timestamp, byte[] signature) @Nullable String text, long timestamp, byte[] signature,
throws DbException { long autoDeleteTimer) throws DbException {
SessionId sessionId = getSessionId(privateGroupId); SessionId sessionId = getSessionId(privateGroupId);
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
@@ -281,7 +281,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
// Handle the invite action // Handle the invite action
session = creatorEngine.onInviteAction(txn, session, text, session = creatorEngine.onInviteAction(txn, session, text,
timestamp, signature); timestamp, signature, autoDeleteTimer);
// Store the updated session // Store the updated session
storeSession(txn, storageId, session); storeSession(txn, storageId, session);
db.commitTransaction(txn); db.commitTransaction(txn);

View File

@@ -17,6 +17,7 @@ import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory; import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
@@ -42,7 +43,8 @@ import static org.briarproject.briar.privategroup.invitation.InviteeState.START;
@NotNullByDefault @NotNullByDefault
class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> { class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
InviteeProtocolEngine(DatabaseComponent db, InviteeProtocolEngine(
DatabaseComponent db,
ClientHelper clientHelper, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
@@ -53,16 +55,18 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
super(db, clientHelper, clientVersioningManager, privateGroupManager, super(db, clientHelper, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, messageParser, messageEncoder, messageTracker,
autoDeleteManager, clock); autoDeleteManager, conversationManager, clock);
} }
@Override @Override
public InviteeSession onInviteAction(Transaction txn, InviteeSession s, public InviteeSession onInviteAction(Transaction txn, InviteeSession s,
@Nullable String text, long timestamp, byte[] signature) { @Nullable String text, long timestamp, byte[] signature,
long autoDeleteTimer) {
throw new UnsupportedOperationException(); // Invalid in this role throw new UnsupportedOperationException(); // Invalid in this role
} }

View File

@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory; import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -49,16 +50,18 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
super(db, clientHelper, clientVersioningManager, privateGroupManager, super(db, clientHelper, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, messageParser, messageEncoder, messageTracker,
autoDeleteManager, clock); autoDeleteManager, conversationManager, clock);
} }
@Override @Override
public PeerSession onInviteAction(Transaction txn, PeerSession s, public PeerSession onInviteAction(Transaction txn, PeerSession s,
@Nullable String text, long timestamp, byte[] signature) { @Nullable String text, long timestamp, byte[] signature,
long autoDeleteTimer) {
throw new UnsupportedOperationException(); // Invalid in this role throw new UnsupportedOperationException(); // Invalid in this role
} }

View File

@@ -8,10 +8,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
interface ProtocolEngine<S extends Session> { interface ProtocolEngine<S extends Session<?>> {
S onInviteAction(Transaction txn, S session, @Nullable String text, S onInviteAction(Transaction txn, S session, @Nullable String text,
long timestamp, byte[] signature) throws DbException; long timestamp, byte[] signature, long autoDeleteTimer)
throws DbException;
S onJoinAction(Transaction txn, S session) throws DbException; S onJoinAction(Transaction txn, S session) throws DbException;

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory; import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -30,6 +31,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
private final MessageEncoder messageEncoder; private final MessageEncoder messageEncoder;
private final MessageTracker messageTracker; private final MessageTracker messageTracker;
private final AutoDeleteManager autoDeleteManager; private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
private final Clock clock; private final Clock clock;
@Inject @Inject
@@ -45,6 +47,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
@@ -57,6 +60,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
this.messageEncoder = messageEncoder; this.messageEncoder = messageEncoder;
this.messageTracker = messageTracker; this.messageTracker = messageTracker;
this.autoDeleteManager = autoDeleteManager; this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
this.clock = clock; this.clock = clock;
} }
@@ -66,7 +70,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
clientVersioningManager, privateGroupManager, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, messageParser, messageEncoder, messageTracker,
autoDeleteManager, clock); autoDeleteManager, conversationManager, clock);
} }
@Override @Override
@@ -75,7 +79,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
clientVersioningManager, privateGroupManager, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, messageParser, messageEncoder, messageTracker,
autoDeleteManager, clock); autoDeleteManager, conversationManager, clock);
} }
@Override @Override
@@ -84,6 +88,6 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
clientVersioningManager, privateGroupManager, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, messageParser, messageEncoder, messageTracker,
autoDeleteManager, clock); autoDeleteManager, conversationManager, clock);
} }
} }

View File

@@ -19,6 +19,7 @@ import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.blog.event.BlogInvitationRequestReceivedEvent; import org.briarproject.briar.api.blog.event.BlogInvitationRequestReceivedEvent;
import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent; import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.conversation.ConversationRequest; import org.briarproject.briar.api.conversation.ConversationRequest;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -41,13 +42,15 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
MessageParser<Blog> messageParser, MessageParser<Blog> messageParser,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock, Clock clock,
BlogManager blogManager, BlogManager blogManager,
InvitationFactory<Blog, BlogInvitationResponse> invitationFactory) { InvitationFactory<Blog, BlogInvitationResponse> invitationFactory) {
super(db, clientHelper, clientVersioningManager, messageEncoder, super(db, clientHelper, clientVersioningManager, messageEncoder,
messageParser, messageTracker, autoDeleteManager, clock, messageParser, messageTracker, autoDeleteManager,
BlogSharingManager.CLIENT_ID, BlogSharingManager.MAJOR_VERSION, conversationManager, clock, BlogSharingManager.CLIENT_ID,
BlogManager.CLIENT_ID, BlogManager.MAJOR_VERSION); BlogSharingManager.MAJOR_VERSION, BlogManager.CLIENT_ID,
BlogManager.MAJOR_VERSION);
this.blogManager = blogManager; this.blogManager = blogManager;
this.invitationFactory = invitationFactory; this.invitationFactory = invitationFactory;
} }

View File

@@ -13,6 +13,7 @@ import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.conversation.ConversationRequest; import org.briarproject.briar.api.conversation.ConversationRequest;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
@@ -41,14 +42,15 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
MessageParser<Forum> messageParser, MessageParser<Forum> messageParser,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock, Clock clock,
ForumManager forumManager, ForumManager forumManager,
InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) { InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) {
super(db, clientHelper, clientVersioningManager, messageEncoder, super(db, clientHelper, clientVersioningManager, messageEncoder,
messageParser, messageTracker, autoDeleteManager, clock, messageParser, messageTracker, autoDeleteManager,
ForumSharingManager.CLIENT_ID, conversationManager, clock, ForumSharingManager.CLIENT_ID,
ForumSharingManager.MAJOR_VERSION, ForumSharingManager.MAJOR_VERSION, ForumManager.CLIENT_ID,
ForumManager.CLIENT_ID, ForumManager.MAJOR_VERSION); ForumManager.MAJOR_VERSION);
this.forumManager = forumManager; this.forumManager = forumManager;
this.invitationFactory = invitationFactory; this.invitationFactory = invitationFactory;
} }

View File

@@ -12,7 +12,7 @@ import javax.annotation.Nullable;
interface ProtocolEngine<S extends Shareable> { interface ProtocolEngine<S extends Shareable> {
Session onInviteAction(Transaction txn, Session session, Session onInviteAction(Transaction txn, Session session,
@Nullable String text, long timestamp) throws DbException; @Nullable String text) throws DbException;
Session onAcceptAction(Transaction txn, Session session) throws DbException; Session onAcceptAction(Transaction txn, Session session) throws DbException;

View File

@@ -21,6 +21,7 @@ import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.sharing.Shareable; import org.briarproject.briar.api.sharing.Shareable;
import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent; import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent;
@@ -29,6 +30,7 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static java.lang.Math.max;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
@@ -58,6 +60,7 @@ abstract class ProtocolEngineImpl<S extends Shareable>
private final MessageEncoder messageEncoder; private final MessageEncoder messageEncoder;
private final MessageTracker messageTracker; private final MessageTracker messageTracker;
private final AutoDeleteManager autoDeleteManager; private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
private final Clock clock; private final Clock clock;
private final ClientId sharingClientId, shareableClientId; private final ClientId sharingClientId, shareableClientId;
private final int sharingClientMajorVersion, shareableClientMajorVersion; private final int sharingClientMajorVersion, shareableClientMajorVersion;
@@ -70,6 +73,7 @@ abstract class ProtocolEngineImpl<S extends Shareable>
MessageParser<S> messageParser, MessageParser<S> messageParser,
MessageTracker messageTracker, MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager, AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock, Clock clock,
ClientId sharingClientId, ClientId sharingClientId,
int sharingClientMajorVersion, int sharingClientMajorVersion,
@@ -82,6 +86,7 @@ abstract class ProtocolEngineImpl<S extends Shareable>
this.messageParser = messageParser; this.messageParser = messageParser;
this.messageTracker = messageTracker; this.messageTracker = messageTracker;
this.autoDeleteManager = autoDeleteManager; this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
this.clock = clock; this.clock = clock;
this.sharingClientId = sharingClientId; this.sharingClientId = sharingClientId;
this.sharingClientMajorVersion = sharingClientMajorVersion; this.sharingClientMajorVersion = sharingClientMajorVersion;
@@ -91,10 +96,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
@Override @Override
public Session onInviteAction(Transaction txn, Session s, public Session onInviteAction(Transaction txn, Session s,
@Nullable String text, long timestamp) throws DbException { @Nullable String text) throws DbException {
switch (s.getState()) { switch (s.getState()) {
case START: case START:
return onLocalInvite(txn, s, text, timestamp); return onLocalInvite(txn, s, text);
case LOCAL_INVITED: case LOCAL_INVITED:
case REMOTE_INVITED: case REMOTE_INVITED:
case SHARING: case SHARING:
@@ -107,9 +112,9 @@ abstract class ProtocolEngineImpl<S extends Shareable>
} }
private Session onLocalInvite(Transaction txn, Session s, private Session onLocalInvite(Transaction txn, Session s,
@Nullable String text, long timestamp) throws DbException { @Nullable String text) throws DbException {
// Send an INVITE message // Send an INVITE message
Message sent = sendInviteMessage(txn, s, text, timestamp); Message sent = sendInviteMessage(txn, s, text);
// Track the message // Track the message
messageTracker.trackOutgoingMessage(txn, sent); messageTracker.trackOutgoingMessage(txn, sent);
// Make the shareable visible to the contact // Make the shareable visible to the contact
@@ -125,7 +130,7 @@ abstract class ProtocolEngineImpl<S extends Shareable>
} }
private Message sendInviteMessage(Transaction txn, Session s, private Message sendInviteMessage(Transaction txn, Session s,
@Nullable String text, long timestamp) throws DbException { @Nullable String text) throws DbException {
Group g = db.getGroup(txn, s.getShareableId()); Group g = db.getGroup(txn, s.getShareableId());
BdfList descriptor; BdfList descriptor;
try { try {
@@ -133,8 +138,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group descriptor throw new DbException(e); // Invalid group descriptor
} }
long localTimestamp = Math.max(timestamp, getLocalTimestamp(s));
Message m; Message m;
long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
@@ -201,16 +206,17 @@ abstract class ProtocolEngineImpl<S extends Shareable>
private Message sendAcceptMessage(Transaction txn, Session s) private Message sendAcceptMessage(Transaction txn, Session s)
throws DbException { throws DbException {
Message m; Message m;
long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(), m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
s.getShareableId(), getLocalTimestamp(s), 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);
} else { } else {
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(), m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
s.getShareableId(), getLocalTimestamp(s), s.getShareableId(), localTimestamp,
s.getLastLocalMessageId()); s.getLastLocalMessageId());
sendMessage(txn, m, ACCEPT, s.getShareableId(), true, sendMessage(txn, m, ACCEPT, s.getShareableId(), true,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
@@ -254,16 +260,17 @@ abstract class ProtocolEngineImpl<S extends Shareable>
private Message sendDeclineMessage(Transaction txn, Session s) private Message sendDeclineMessage(Transaction txn, Session s)
throws DbException { throws DbException {
Message m; Message m;
long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(), m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
s.getShareableId(), getLocalTimestamp(s), 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);
} else { } else {
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(), m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
s.getShareableId(), getLocalTimestamp(s), s.getShareableId(), localTimestamp,
s.getLastLocalMessageId()); s.getLastLocalMessageId());
sendMessage(txn, m, DECLINE, s.getShareableId(), true, sendMessage(txn, m, DECLINE, s.getShareableId(), true,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
@@ -307,9 +314,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
private Message sendLeaveMessage(Transaction txn, Session session) private Message sendLeaveMessage(Transaction txn, Session session)
throws DbException { throws DbException {
long localTimestamp = getTimestampForInvisibleMessage(session);
Message m = messageEncoder.encodeLeaveMessage( Message m = messageEncoder.encodeLeaveMessage(
session.getContactGroupId(), session.getShareableId(), session.getContactGroupId(), session.getShareableId(),
getLocalTimestamp(session), session.getLastLocalMessageId()); localTimestamp, session.getLastLocalMessageId());
sendMessage(txn, m, LEAVE, session.getShareableId(), false, sendMessage(txn, m, LEAVE, session.getShareableId(), false,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
return m; return m;
@@ -605,9 +613,10 @@ abstract class ProtocolEngineImpl<S extends Shareable>
private Message sendAbortMessage(Transaction txn, Session session) private Message sendAbortMessage(Transaction txn, Session session)
throws DbException { throws DbException {
long localTimestamp = getTimestampForInvisibleMessage(session);
Message m = messageEncoder.encodeAbortMessage( Message m = messageEncoder.encodeAbortMessage(
session.getContactGroupId(), session.getShareableId(), session.getContactGroupId(), session.getShareableId(),
getLocalTimestamp(session), session.getLastLocalMessageId()); localTimestamp, session.getLastLocalMessageId());
sendMessage(txn, m, ABORT, session.getShareableId(), false, sendMessage(txn, m, ABORT, session.getShareableId(), false,
NO_AUTO_DELETE_TIMER); NO_AUTO_DELETE_TIMER);
return m; return m;
@@ -677,10 +686,34 @@ abstract class ProtocolEngineImpl<S extends Shareable>
return !dependency.equals(expected); return !dependency.equals(expected);
} }
private long getLocalTimestamp(Session session) { /**
return Math.max(clock.currentTimeMillis(), * Returns a timestamp for a visible outgoing message. The timestamp is
Math.max(session.getLocalTimestamp(), * later than the timestamp of any message sent or received so far in the
session.getInviteTimestamp()) + 1); * conversation, and later than the {@link #getSessionTimestamp(Session)
* session timestamp}.
*/
private long getTimestampForVisibleMessage(Transaction txn, Session s)
throws DbException {
ContactId c = getContactId(txn, s.getContactGroupId());
long conversationTimestamp =
conversationManager.getTimestampForOutgoingMessage(txn, c);
return max(conversationTimestamp, getSessionTimestamp(s) + 1);
}
/**
* Returns a timestamp for an invisible outgoing message. The timestamp is
* later than the {@link #getSessionTimestamp(Session) session timestamp}.
*/
private long getTimestampForInvisibleMessage(Session s) {
return max(clock.currentTimeMillis(), getSessionTimestamp(s) + 1);
}
/**
* Returns the latest timestamp of any message sent so far in the session,
* and any invite message sent or received so far in the session.
*/
private long getSessionTimestamp(Session s) {
return max(s.getLocalTimestamp(), s.getInviteTimestamp());
} }
private ContactId getContactId(Transaction txn, GroupId contactGroupId) private ContactId getContactId(Transaction txn, GroupId contactGroupId)

View File

@@ -248,7 +248,7 @@ abstract class SharingManagerImpl<S extends Shareable>
@Override @Override
public void sendInvitation(GroupId shareableId, ContactId contactId, public void sendInvitation(GroupId shareableId, ContactId contactId,
@Nullable String text, long timestamp) throws DbException { @Nullable String text) throws DbException {
SessionId sessionId = getSessionId(shareableId); SessionId sessionId = getSessionId(shareableId);
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
@@ -273,7 +273,7 @@ abstract class SharingManagerImpl<S extends Shareable>
storageId = ss.storageId; storageId = ss.storageId;
} }
// Handle the invite action // Handle the invite action
session = engine.onInviteAction(txn, session, text, timestamp); session = engine.onInviteAction(txn, session, text);
// Store the updated session // Store the updated session
storeSession(txn, storageId, session); storeSession(txn, storageId, session);
db.commitTransaction(txn); db.commitTransaction(txn);

View File

@@ -45,8 +45,7 @@ public class ForumManagerTest
forum0 = forumManager0.addForum("Test Forum"); forum0 = forumManager0.addForum("Test Forum");
groupId0 = forum0.getId(); groupId0 = forum0.getId();
// share forum // share forum
forumSharingManager0.sendInvitation(groupId0, contactId1From0, null, forumSharingManager0.sendInvitation(groupId0, contactId1From0, null);
clock.currentTimeMillis());
sync0To1(1, true); sync0To1(1, true);
forumSharingManager1.respondToInvitation(forum0, contact0From1, true); forumSharingManager1.respondToInvitation(forum0, contact0From1, true);
sync1To0(1, true); sync1To0(1, true);
@@ -194,8 +193,7 @@ public class ForumManagerTest
// share a second forum // share a second forum
Forum forum1 = forumManager0.addForum("Test Forum1"); Forum forum1 = forumManager0.addForum("Test Forum1");
GroupId g1 = forum1.getId(); GroupId g1 = forum1.getId();
forumSharingManager0.sendInvitation(g1, contactId1From0, null, forumSharingManager0.sendInvitation(g1, contactId1From0, null);
clock.currentTimeMillis());
sync0To1(1, true); sync0To1(1, true);
forumSharingManager1.respondToInvitation(forum1, contact0From1, true); forumSharingManager1.respondToInvitation(forum1, contact0From1, true);
sync1To0(1, true); sync1To0(1, true);

View File

@@ -140,11 +140,9 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contact1From0; Contact introducee1 = contact1From0;
Contact introducee2 = contact2From0; Contact introducee2 = contact2From0;
introductionManager0 introductionManager0.makeIntroduction(introducee1, introducee2, "Hi!");
.makeIntroduction(introducee1, introducee2, "Hi!", time);
// check that messages are tracked properly // check that messages are tracked properly
Group g1 = introductionManager0.getContactGroup(introducee1); Group g1 = introductionManager0.getContactGroup(introducee1);
@@ -264,11 +262,9 @@ public class IntroductionIntegrationTest
addListeners(false, true); addListeners(false, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contact1From0; Contact introducee1 = contact1From0;
Contact introducee2 = contact2From0; Contact introducee2 = contact2From0;
introductionManager0 introductionManager0.makeIntroduction(introducee1, introducee2, null);
.makeIntroduction(introducee1, introducee2, null, time);
// sync request messages // sync request messages
sync0To1(1, true); sync0To1(1, true);
@@ -356,9 +352,8 @@ public class IntroductionIntegrationTest
addListeners(true, false); addListeners(true, false);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync request messages // sync request messages
sync0To1(1, true); sync0To1(1, true);
@@ -412,9 +407,8 @@ public class IntroductionIntegrationTest
addListeners(false, true); addListeners(false, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync request messages // sync request messages
sync0To1(1, true); sync0To1(1, true);
@@ -438,9 +432,8 @@ public class IntroductionIntegrationTest
assertFalse(listener1.aborted); assertFalse(listener1.aborted);
assertFalse(listener2.aborted); assertFalse(listener2.aborted);
time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync request messages // sync request messages
sync0To1(1, true); sync0To1(1, true);
@@ -457,9 +450,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time); .makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -482,7 +474,7 @@ public class IntroductionIntegrationTest
// answer request manually // answer request manually
introductionManager2.respondToIntroduction(contactId0From2, introductionManager2.respondToIntroduction(contactId0From2,
listener2.sessionId, time, true); listener2.sessionId, true);
// sync second response and AUTH // sync second response and AUTH
sync2To0(2, true); sync2To0(2, true);
@@ -518,11 +510,10 @@ public class IntroductionIntegrationTest
listener2.answerRequests = false; listener2.answerRequests = false;
// make introduction // make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contact1From0; Contact introducee1 = contact1From0;
Contact introducee2 = contact2From0; Contact introducee2 = contact2From0;
introductionManager0 introductionManager0
.makeIntroduction(introducee1, introducee2, null, time); .makeIntroduction(introducee1, introducee2, null);
// sync request messages // sync request messages
sync0To1(1, true); sync0To1(1, true);
@@ -564,7 +555,7 @@ public class IntroductionIntegrationTest
// answer request manually // answer request manually
introductionManager2.respondToIntroduction(contactId0From2, introductionManager2.respondToIntroduction(contactId0From2,
listener2.sessionId, time, false); listener2.sessionId, false);
// now introducee2 should have returned to the START state // now introducee2 should have returned to the START state
introduceeSession = getIntroduceeSession(c2); introduceeSession = getIntroduceeSession(c2);
@@ -611,9 +602,8 @@ public class IntroductionIntegrationTest
addListeners(true, false); addListeners(true, false);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact1From0, null, time); .makeIntroduction(contact1From0, contact1From0, null);
// sync request messages // sync request messages
sync0To1(1, false); sync0To1(1, false);
@@ -637,9 +627,8 @@ public class IntroductionIntegrationTest
.canIntroduce(contact1From0, contact2From0)); .canIntroduce(contact1From0, contact2From0));
// make the introduction // make the introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// no more introduction allowed while the existing one is in progress // no more introduction allowed while the existing one is in progress
assertFalse(introductionManager0 assertFalse(introductionManager0
@@ -647,7 +636,7 @@ public class IntroductionIntegrationTest
// try it anyway and fail // try it anyway and fail
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
} }
@Test @Test
@@ -661,9 +650,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make the introduction // make the introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST messages // sync REQUEST messages
sync0To1(1, true); sync0To1(1, true);
@@ -719,9 +707,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make the introduction // make the introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST messages // sync REQUEST messages
sync0To1(1, true); sync0To1(1, true);
@@ -766,9 +753,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make the introduction // make the introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST to introducee1 // sync REQUEST to introducee1
sync0To1(1, true); sync0To1(1, true);
@@ -803,9 +789,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make the introduction // make the introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST to introducee1 // sync REQUEST to introducee1
sync0To1(1, true); sync0To1(1, true);
@@ -838,9 +823,8 @@ public class IntroductionIntegrationTest
addListeners(false, true); addListeners(false, true);
// make the introduction // make the introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST to introducee1 // sync REQUEST to introducee1
sync0To1(1, true); sync0To1(1, true);
@@ -873,9 +857,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make the introduction // make the introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// sync REQUEST messages // sync REQUEST messages
sync0To1(1, true); sync0To1(1, true);
@@ -914,9 +897,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time); .makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -943,9 +925,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time); .makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -987,9 +968,8 @@ public class IntroductionIntegrationTest
@Test @Test
public void testIntroductionAfterReAddingContacts() throws Exception { public void testIntroductionAfterReAddingContacts() throws Exception {
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// 0 and 1 remove and re-add each other // 0 and 1 remove and re-add each other
contactManager0.removeContact(contactId1From0); contactManager0.removeContact(contactId1From0);
@@ -1016,9 +996,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make new introduction // make new introduction
time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, null, time); .makeIntroduction(contact1From0, contact2From0, null);
// introduction should sync and not be INVALID or PENDING // introduction should sync and not be INVALID or PENDING
sync0To1(1, true); sync0To1(1, true);
@@ -1032,9 +1011,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time); .makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync request messages // sync request messages
sync0To1(1, true); sync0To1(1, true);
@@ -1147,9 +1125,8 @@ public class IntroductionIntegrationTest
addListeners(true, true); addListeners(true, true);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time); .makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync first REQUEST message // sync first REQUEST message
sync0To1(1, true); sync0To1(1, true);
@@ -1292,7 +1269,7 @@ public class IntroductionIntegrationTest
assertTrue(introductionManager0 assertTrue(introductionManager0
.canIntroduce(contact1From0, contact2From0)); .canIntroduce(contact1From0, contact2From0));
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Ho!", time); .makeIntroduction(contact1From0, contact2From0, "Ho!");
sync0To1(1, true); sync0To1(1, true);
sync0To2(1, true); sync0To2(1, true);
@@ -1332,9 +1309,8 @@ public class IntroductionIntegrationTest
addListeners(false, false); addListeners(false, false);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time); .makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync REQUEST messages // sync REQUEST messages
sync0To1(1, true); sync0To1(1, true);
@@ -1399,9 +1375,8 @@ public class IntroductionIntegrationTest
// a new introduction is still possible // a new introduction is still possible
assertTrue(introductionManager0 assertTrue(introductionManager0
.canIntroduce(contact1From0, contact2From0)); .canIntroduce(contact1From0, contact2From0));
time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Ho!", time); .makeIntroduction(contact1From0, contact2From0, "Ho!");
sync0To1(1, true); sync0To1(1, true);
sync0To2(1, true); sync0To2(1, true);
@@ -1428,9 +1403,8 @@ public class IntroductionIntegrationTest
addListeners(false, false); addListeners(false, false);
// make introduction // make introduction
long time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time); .makeIntroduction(contact1From0, contact2From0, "Hi!");
// sync REQUEST messages // sync REQUEST messages
sync0To1(1, true); sync0To1(1, true);
@@ -1458,9 +1432,8 @@ public class IntroductionIntegrationTest
// a new introduction is still possible // a new introduction is still possible
assertTrue(introductionManager0 assertTrue(introductionManager0
.canIntroduce(contact1From0, contact2From0)); .canIntroduce(contact1From0, contact2From0));
time = clock.currentTimeMillis();
introductionManager0 introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Ho!", time); .makeIntroduction(contact1From0, contact2From0, "Ho!");
sync0To1(1, true); sync0To1(1, true);
sync0To2(1, true); sync0To2(1, true);
@@ -1496,9 +1469,8 @@ public class IntroductionIntegrationTest
addListeners(false, false); addListeners(false, false);
// make introduction // make introduction
long time = clock.currentTimeMillis(); introductionManager0
introductionManager0.makeIntroduction(contact1From0, contact2From0, .makeIntroduction(contact1From0, contact2From0, "Hi!");
"Hi!", time);
// deleting the introduction for introducee1 will fail // deleting the introduction for introducee1 will fail
Collection<ConversationMessageHeader> m1From0 = getMessages1From0(); Collection<ConversationMessageHeader> m1From0 = getMessages1From0();
@@ -1795,16 +1767,13 @@ public class IntroductionIntegrationTest
IntroductionRequest ir = introEvent.getMessageHeader(); IntroductionRequest ir = introEvent.getMessageHeader();
ContactId contactId = introEvent.getContactId(); ContactId contactId = introEvent.getContactId();
sessionId = ir.getSessionId(); sessionId = ir.getSessionId();
long time = clock.currentTimeMillis();
try { try {
if (introducee == 1 && answerRequests) { if (introducee == 1 && answerRequests) {
introductionManager1 introductionManager1.respondToIntroduction(contactId,
.respondToIntroduction(contactId, sessionId, sessionId, accept);
time, accept);
} else if (introducee == 2 && answerRequests) { } else if (introducee == 2 && answerRequests) {
introductionManager2 introductionManager2.respondToIntroduction(contactId,
.respondToIntroduction(contactId, sessionId, sessionId, accept);
time, accept);
} }
} catch (DbException exception) { } catch (DbException exception) {
eventWaiter.rethrow(exception); eventWaiter.rethrow(exception);

View File

@@ -25,6 +25,7 @@ import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE; import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_CONTACT; import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_US; import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_US;
@@ -220,8 +221,8 @@ public class PrivateGroupIntegrationTest
byte[] signature = groupInvitationFactory byte[] signature = groupInvitationFactory
.signInvitation(contact, groupId0, timestamp, .signInvitation(contact, groupId0, timestamp,
author0.getPrivateKey()); author0.getPrivateKey());
groupInvitationManager0 groupInvitationManager0.sendInvitation(groupId0, c, text, timestamp,
.sendInvitation(groupId0, c, text, timestamp, signature); signature, NO_AUTO_DELETE_TIMER);
} }
private GroupMember getGroupMember(PrivateGroupManager groupManager, private GroupMember getGroupMember(PrivateGroupManager groupManager,

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory; import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
@@ -62,6 +63,8 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
final MessageTracker messageTracker = context.mock(MessageTracker.class); final MessageTracker messageTracker = context.mock(MessageTracker.class);
final AutoDeleteManager autoDeleteManager = final AutoDeleteManager autoDeleteManager =
context.mock(AutoDeleteManager.class); context.mock(AutoDeleteManager.class);
final ConversationManager conversationManager =
context.mock(ConversationManager.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
@@ -115,16 +118,26 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
assertEquals(inviteTimestamp, s.getInviteTimestamp()); assertEquals(inviteTimestamp, s.getInviteTimestamp());
} }
void expectGetLocalTimestamp(long time) { void expectGetTimestampForInvisibleMessage(long time) {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(time)); will(returnValue(time));
}}); }});
} }
void expectGetTimestampForVisibleMessage(long time) throws Exception {
context.checking(new Expectations() {{
oneOf(clientHelper).getContactId(txn, contactGroupId);
will(returnValue(contactId));
oneOf(conversationManager)
.getTimestampForOutgoingMessage(txn, contactId);
will(returnValue(time));
}});
}
void expectSendInviteMessage(String text) throws Exception { void expectSendInviteMessage(String text) throws Exception {
expectCheckWhetherContactSupportsAutoDeletion(true); expectGetTimestampForVisibleMessage(messageTimestamp);
expectGetLocalTimestamp(messageTimestamp); expectCheckWhetherContactSupportsAutoDeletion();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageEncoder).encodeInviteMessage(contactGroupId, oneOf(messageEncoder).encodeInviteMessage(contactGroupId,
privateGroupId, inviteTimestamp, privateGroup.getName(), privateGroupId, inviteTimestamp, privateGroup.getName(),
@@ -137,8 +150,10 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
void expectSendJoinMessage(JoinMessage m, boolean visible) void expectSendJoinMessage(JoinMessage m, boolean visible)
throws Exception { throws Exception {
expectCheckWhetherContactSupportsAutoDeletion(visible); if (visible) expectGetTimestampForVisibleMessage(messageTimestamp);
expectGetLocalTimestamp(messageTimestamp); else expectGetTimestampForInvisibleMessage(messageTimestamp);
expectCheckWhetherContactSupportsAutoDeletion();
if (visible) expectGetAutoDeleteTimer();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageEncoder).encodeJoinMessage(m.getContactGroupId(), oneOf(messageEncoder).encodeJoinMessage(m.getContactGroupId(),
m.getPrivateGroupId(), m.getTimestamp(), m.getPrivateGroupId(), m.getTimestamp(),
@@ -149,8 +164,10 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
} }
void expectSendLeaveMessage(boolean visible) throws Exception { void expectSendLeaveMessage(boolean visible) throws Exception {
expectCheckWhetherContactSupportsAutoDeletion(visible); if (visible) expectGetTimestampForVisibleMessage(messageTimestamp);
expectGetLocalTimestamp(messageTimestamp); else expectGetTimestampForInvisibleMessage(messageTimestamp);
expectCheckWhetherContactSupportsAutoDeletion();
if (visible) expectGetAutoDeleteTimer();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageEncoder).encodeLeaveMessage(contactGroupId, oneOf(messageEncoder).encodeLeaveMessage(contactGroupId,
privateGroupId, messageTimestamp, lastLocalMessageId, privateGroupId, messageTimestamp, lastLocalMessageId,
@@ -161,7 +178,7 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
} }
void expectSendAbortMessage() throws Exception { void expectSendAbortMessage() throws Exception {
expectGetLocalTimestamp(messageTimestamp); expectGetTimestampForInvisibleMessage(messageTimestamp);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageEncoder) oneOf(messageEncoder)
.encodeAbortMessage(contactGroupId, privateGroupId, .encodeAbortMessage(contactGroupId, privateGroupId,
@@ -226,8 +243,7 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
}}); }});
} }
void expectCheckWhetherContactSupportsAutoDeletion(boolean visible) void expectCheckWhetherContactSupportsAutoDeletion() throws Exception {
throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper).getContactId(txn, contactGroupId); oneOf(clientHelper).getContactId(txn, contactGroupId);
will(returnValue(contactId)); will(returnValue(contactId));
@@ -235,10 +251,13 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
GroupInvitationManager.CLIENT_ID, GroupInvitationManager.CLIENT_ID,
GroupInvitationManager.MAJOR_VERSION); GroupInvitationManager.MAJOR_VERSION);
will(returnValue(GroupInvitationManager.MINOR_VERSION)); will(returnValue(GroupInvitationManager.MINOR_VERSION));
if (visible) { }});
oneOf(autoDeleteManager).getAutoDeleteTimer(txn, contactId); }
will(returnValue(NO_AUTO_DELETE_TIMER));
} void expectGetAutoDeleteTimer() throws Exception {
context.checking(new Expectations() {{
oneOf(autoDeleteManager).getAutoDeleteTimer(txn, contactId);
will(returnValue(NO_AUTO_DELETE_TIMER));
}}); }});
} }
} }

View File

@@ -24,7 +24,8 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
new CreatorProtocolEngine(db, clientHelper, clientVersioningManager, new CreatorProtocolEngine(db, clientHelper, clientVersioningManager,
privateGroupManager, privateGroupFactory, privateGroupManager, privateGroupFactory,
groupMessageFactory, identityManager, messageParser, groupMessageFactory, identityManager, messageParser,
messageEncoder, messageTracker, autoDeleteManager, clock); messageEncoder, messageTracker, autoDeleteManager,
conversationManager, clock);
private CreatorSession getDefaultSession(CreatorState state) { private CreatorSession getDefaultSession(CreatorState state) {
return new CreatorSession(contactGroupId, privateGroupId, return new CreatorSession(contactGroupId, privateGroupId,
@@ -43,7 +44,7 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
expectOnLocalInvite(text); expectOnLocalInvite(text);
CreatorSession newSession = CreatorSession newSession =
engine.onInviteAction(txn, session, text, inviteTimestamp, engine.onInviteAction(txn, session, text, inviteTimestamp,
signature); signature, NO_AUTO_DELETE_TIMER);
assertEquals(INVITED, newSession.getState()); assertEquals(INVITED, newSession.getState());
assertEquals(messageId, newSession.getLastLocalMessageId()); assertEquals(messageId, newSession.getLastLocalMessageId());
assertNull(newSession.getLastRemoteMessageId()); assertNull(newSession.getLastRemoteMessageId());
@@ -60,7 +61,7 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
expectOnLocalInvite(null); expectOnLocalInvite(null);
CreatorSession newSession = CreatorSession newSession =
engine.onInviteAction(txn, session, null, inviteTimestamp, engine.onInviteAction(txn, session, null, inviteTimestamp,
signature); signature, NO_AUTO_DELETE_TIMER);
assertEquals(INVITED, newSession.getState()); assertEquals(INVITED, newSession.getState());
assertEquals(messageId, newSession.getLastLocalMessageId()); assertEquals(messageId, newSession.getLastLocalMessageId());
assertNull(newSession.getLastRemoteMessageId()); assertNull(newSession.getLastRemoteMessageId());
@@ -83,31 +84,31 @@ public class CreatorProtocolEngineTest extends AbstractProtocolEngineTest {
@Test(expected = ProtocolStateException.class) @Test(expected = ProtocolStateException.class)
public void testOnInviteActionFromInvited() throws Exception { public void testOnInviteActionFromInvited() throws Exception {
engine.onInviteAction(txn, getDefaultSession(INVITED), null, engine.onInviteAction(txn, getDefaultSession(INVITED), null,
inviteTimestamp, signature); inviteTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = ProtocolStateException.class) @Test(expected = ProtocolStateException.class)
public void testOnInviteActionFromJoined() throws Exception { public void testOnInviteActionFromJoined() throws Exception {
engine.onInviteAction(txn, getDefaultSession(JOINED), null, engine.onInviteAction(txn, getDefaultSession(JOINED), null,
inviteTimestamp, signature); inviteTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = ProtocolStateException.class) @Test(expected = ProtocolStateException.class)
public void testOnInviteActionFromLeft() throws Exception { public void testOnInviteActionFromLeft() throws Exception {
engine.onInviteAction(txn, getDefaultSession(LEFT), null, engine.onInviteAction(txn, getDefaultSession(LEFT), null,
inviteTimestamp, signature); inviteTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = ProtocolStateException.class) @Test(expected = ProtocolStateException.class)
public void testOnInviteActionFromDissolved() throws Exception { public void testOnInviteActionFromDissolved() throws Exception {
engine.onInviteAction(txn, getDefaultSession(DISSOLVED), null, engine.onInviteAction(txn, getDefaultSession(DISSOLVED), null,
inviteTimestamp, signature); inviteTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = ProtocolStateException.class) @Test(expected = ProtocolStateException.class)
public void testOnInviteActionFromError() throws Exception { public void testOnInviteActionFromError() throws Exception {
engine.onInviteAction(txn, getDefaultSession(ERROR), null, engine.onInviteAction(txn, getDefaultSession(ERROR), null,
inviteTimestamp, signature); inviteTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
// onJoinAction // onJoinAction

View File

@@ -27,6 +27,7 @@ import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.util.Collections.emptySet; import static java.util.Collections.emptySet;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount; import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -687,7 +688,8 @@ public class GroupInvitationIntegrationTest
byte[] signature = groupInvitationFactory.signInvitation(contact1From0, byte[] signature = groupInvitationFactory.signInvitation(contact1From0,
privateGroup.getId(), timestamp, author0.getPrivateKey()); privateGroup.getId(), timestamp, author0.getPrivateKey());
groupInvitationManager0.sendInvitation(privateGroup.getId(), groupInvitationManager0.sendInvitation(privateGroup.getId(),
contactId1From0, text, timestamp, signature); contactId1From0, text, timestamp, signature,
NO_AUTO_DELETE_TIMER);
} }
} }

View File

@@ -482,7 +482,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(creatorEngine).onInviteAction(with(txn), oneOf(creatorEngine).onInviteAction(with(txn),
with(any(CreatorSession.class)), with(text), with(time), with(any(CreatorSession.class)), with(text), with(time),
with(signature)); with(signature), with(NO_AUTO_DELETE_TIMER));
will(returnValue(creatorSession)); will(returnValue(creatorSession));
}}); }});
expectStoreSession(creatorSession, storageMessage.getId()); expectStoreSession(creatorSession, storageMessage.getId());
@@ -491,7 +491,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
groupInvitationManager.sendInvitation(privateGroup.getId(), contactId, groupInvitationManager.sendInvitation(privateGroup.getId(), contactId,
text, time, signature); text, time, signature, NO_AUTO_DELETE_TIMER);
} }
@Test @Test
@@ -514,7 +514,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
will(returnValue(creatorSession)); will(returnValue(creatorSession));
oneOf(creatorEngine).onInviteAction(with(txn), oneOf(creatorEngine).onInviteAction(with(txn),
with(any(CreatorSession.class)), with(text), with(time), with(any(CreatorSession.class)), with(text), with(time),
with(signature)); with(signature), with(NO_AUTO_DELETE_TIMER));
will(returnValue(creatorSession)); will(returnValue(creatorSession));
}}); }});
expectStoreSession(creatorSession, storageMessage.getId()); expectStoreSession(creatorSession, storageMessage.getId());
@@ -523,7 +523,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
groupInvitationManager.sendInvitation(privateGroup.getId(), contactId, groupInvitationManager.sendInvitation(privateGroup.getId(), contactId,
text, time, signature); text, time, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)

View File

@@ -43,7 +43,8 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
new InviteeProtocolEngine(db, clientHelper, clientVersioningManager, new InviteeProtocolEngine(db, clientHelper, clientVersioningManager,
privateGroupManager, privateGroupFactory, privateGroupManager, privateGroupFactory,
groupMessageFactory, identityManager, messageParser, groupMessageFactory, identityManager, messageParser,
messageEncoder, messageTracker, autoDeleteManager, clock); messageEncoder, messageTracker, autoDeleteManager,
conversationManager, clock);
private final LocalAuthor localAuthor = getLocalAuthor(); private final LocalAuthor localAuthor = getLocalAuthor();
private InviteeSession getDefaultSession(InviteeState state) { private InviteeSession getDefaultSession(InviteeState state) {
@@ -57,43 +58,43 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromStart() { public void testOnInviteActionFromStart() {
engine.onInviteAction(txn, getDefaultSession(START), null, engine.onInviteAction(txn, getDefaultSession(START), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromLeft() { public void testOnInviteActionFromLeft() {
engine.onInviteAction(txn, getDefaultSession(ACCEPTED), null, engine.onInviteAction(txn, getDefaultSession(ACCEPTED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromInvited() { public void testOnInviteActionFromInvited() {
engine.onInviteAction(txn, getDefaultSession(INVITED), null, engine.onInviteAction(txn, getDefaultSession(INVITED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromDissolved() { public void testOnInviteActionFromDissolved() {
engine.onInviteAction(txn, getDefaultSession(DISSOLVED), null, engine.onInviteAction(txn, getDefaultSession(DISSOLVED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromAccepted() { public void testOnInviteActionFromAccepted() {
engine.onInviteAction(txn, getDefaultSession(ACCEPTED), null, engine.onInviteAction(txn, getDefaultSession(ACCEPTED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromJoined() { public void testOnInviteActionFromJoined() {
engine.onInviteAction(txn, getDefaultSession(JOINED), null, engine.onInviteAction(txn, getDefaultSession(JOINED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromError() { public void testOnInviteActionFromError() {
engine.onInviteAction(txn, getDefaultSession(ERROR), null, engine.onInviteAction(txn, getDefaultSession(ERROR), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
// onJoinAction // onJoinAction

View File

@@ -27,7 +27,8 @@ public class PeerProtocolEngineTest extends AbstractProtocolEngineTest {
new PeerProtocolEngine(db, clientHelper, clientVersioningManager, new PeerProtocolEngine(db, clientHelper, clientVersioningManager,
privateGroupManager, privateGroupFactory, privateGroupManager, privateGroupFactory,
groupMessageFactory, identityManager, messageParser, groupMessageFactory, identityManager, messageParser,
messageEncoder, messageTracker, autoDeleteManager, clock); messageEncoder, messageTracker, autoDeleteManager,
conversationManager, clock);
private PeerSession getDefaultSession(PeerState state) { private PeerSession getDefaultSession(PeerState state) {
return new PeerSession(contactGroupId, privateGroupId, return new PeerSession(contactGroupId, privateGroupId,
@@ -39,43 +40,43 @@ public class PeerProtocolEngineTest extends AbstractProtocolEngineTest {
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromStart() { public void testOnInviteActionFromStart() {
engine.onInviteAction(txn, getDefaultSession(START), null, engine.onInviteAction(txn, getDefaultSession(START), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromAwaitMember() { public void testOnInviteActionFromAwaitMember() {
engine.onInviteAction(txn, getDefaultSession(AWAIT_MEMBER), null, engine.onInviteAction(txn, getDefaultSession(AWAIT_MEMBER), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromNeitherJoined() { public void testOnInviteActionFromNeitherJoined() {
engine.onInviteAction(txn, getDefaultSession(NEITHER_JOINED), null, engine.onInviteAction(txn, getDefaultSession(NEITHER_JOINED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromLocalJoined() { public void testOnInviteActionFromLocalJoined() {
engine.onInviteAction(txn, getDefaultSession(LOCAL_JOINED), null, engine.onInviteAction(txn, getDefaultSession(LOCAL_JOINED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromBothJoined() { public void testOnInviteActionFromBothJoined() {
engine.onInviteAction(txn, getDefaultSession(BOTH_JOINED), null, engine.onInviteAction(txn, getDefaultSession(BOTH_JOINED), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromLocalLeft() { public void testOnInviteActionFromLocalLeft() {
engine.onInviteAction(txn, getDefaultSession(LOCAL_LEFT), null, engine.onInviteAction(txn, getDefaultSession(LOCAL_LEFT), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void testOnInviteActionFromError() { public void testOnInviteActionFromError() {
engine.onInviteAction(txn, getDefaultSession(ERROR), null, engine.onInviteAction(txn, getDefaultSession(ERROR), null,
messageTimestamp, signature); messageTimestamp, signature, NO_AUTO_DELETE_TIMER);
} }
// onJoinAction // onJoinAction

View File

@@ -122,8 +122,7 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!", .sendInvitation(blog2.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// invitee has own blog and that of the sharer // invitee has own blog and that of the sharer
assertEquals(2, blogManager1.getBlogs().size()); assertEquals(2, blogManager1.getBlogs().size());
@@ -213,8 +212,8 @@ public class BlogSharingIntegrationTest
blogManager0.addBlog(rssBlog); blogManager0.addBlog(rssBlog);
// send invitation // send invitation
blogSharingManager0.sendInvitation(rssBlog.getId(), contactId1From0, blogSharingManager0
"Hi!", clock.currentTimeMillis()); .sendInvitation(rssBlog.getId(), contactId1From0, "Hi!");
// invitee has own blog and that of the sharer // invitee has own blog and that of the sharer
assertEquals(2, blogManager1.getBlogs().size()); assertEquals(2, blogManager1.getBlogs().size());
@@ -285,8 +284,7 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, null, .sendInvitation(blog2.getId(), contactId1From0, null);
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -341,8 +339,7 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!", .sendInvitation(blog2.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -398,8 +395,7 @@ public class BlogSharingIntegrationTest
// sharer sends invitation for 2's blog to 1 // sharer sends invitation for 2's blog to 1
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!", .sendInvitation(blog2.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -436,8 +432,7 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!", .sendInvitation(blog2.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -515,8 +510,7 @@ public class BlogSharingIntegrationTest
// sharer sends invitation for 2's blog to 1 // sharer sends invitation for 2's blog to 1
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!", .sendInvitation(blog2.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);

View File

@@ -126,8 +126,7 @@ public class ForumSharingIntegrationTest
public void testSuccessfulSharing() throws Exception { public void testSuccessfulSharing() throws Exception {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum.getId(), contactId1From0, "Hi!", .sendInvitation(forum.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// check that request message state is correct // check that request message state is correct
Collection<ConversationMessageHeader> messages = getMessages1From0(); Collection<ConversationMessageHeader> messages = getMessages1From0();
@@ -191,8 +190,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testDeclinedSharing() throws Exception { public void testDeclinedSharing() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -243,8 +242,8 @@ public class ForumSharingIntegrationTest
// send a new invitation again after re-adding the forum // send a new invitation again after re-adding the forum
db0.transaction(false, txn -> forumManager0.addForum(txn, forum)); db0.transaction(false, txn -> forumManager0.addForum(txn, forum));
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
// reset listener state for new request // reset listener state for new request
listener1.requestReceived = false; listener1.requestReceived = false;
@@ -259,8 +258,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testInviteeLeavesAfterFinished() throws Exception { public void testInviteeLeavesAfterFinished() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
"Hi!", clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, "Hi!");
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -322,8 +321,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testSharerLeavesAfterFinished() throws Exception { public void testSharerLeavesAfterFinished() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -383,8 +382,8 @@ public class ForumSharingIntegrationTest
// send a new invitation again after re-adding the forum // send a new invitation again after re-adding the forum
db0.transaction(false, txn -> forumManager0.addForum(txn, forum)); db0.transaction(false, txn -> forumManager0.addForum(txn, forum));
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
// reset listener state for new request // reset listener state for new request
listener1.requestReceived = false; listener1.requestReceived = false;
@@ -399,8 +398,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testSharerLeavesBeforeResponse() throws Exception { public void testSharerLeavesBeforeResponse() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
// sharer un-subscribes from forum // sharer un-subscribes from forum
forumManager0.removeForum(forum); forumManager0.removeForum(forum);
@@ -420,8 +419,7 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum.getId(), contactId1From0, null, .sendInvitation(forum.getId(), contactId1From0, null);
clock.currentTimeMillis());
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -445,8 +443,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testSharingSameForumWithEachOther() throws Exception { public void testSharingSameForumWithEachOther() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
"Hi!", clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, "Hi!");
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -471,10 +469,9 @@ public class ForumSharingIntegrationTest
assertEquals(1, forumManager1.getForums().size()); assertEquals(1, forumManager1.getForums().size());
// invitee now shares same forum back // invitee now shares same forum back
forumSharingManager1.sendInvitation(forum.getId(), forumSharingManager1
contactId0From1, .sendInvitation(forum.getId(), contactId0From1,
"I am re-sharing this forum with you.", "I am re-sharing this forum with you.");
clock.currentTimeMillis());
// assert that the last invitation wasn't send // assert that the last invitation wasn't send
assertEquals(2, c1.getMessageTracker().getGroupCount(group.getId()) assertEquals(2, c1.getMessageTracker().getGroupCount(group.getId())
@@ -485,8 +482,8 @@ public class ForumSharingIntegrationTest
public void testSharingSameForumWithEachOtherBeforeAccept() public void testSharingSameForumWithEachOtherBeforeAccept()
throws Exception { throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
"Hi!", clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, "Hi!");
sync0To1(1, true); sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertRequestReceived(listener1, contactId0From1); assertRequestReceived(listener1, contactId0From1);
@@ -501,10 +498,9 @@ public class ForumSharingIntegrationTest
.getMsgCount()); .getMsgCount());
// invitee now shares same forum back // invitee now shares same forum back
forumSharingManager1.sendInvitation(forum.getId(), forumSharingManager1
contactId0From1, .sendInvitation(forum.getId(), contactId0From1,
"I am re-sharing this forum with you.", "I am re-sharing this forum with you.");
clock.currentTimeMillis());
// assert that the last invitation wasn't send // assert that the last invitation wasn't send
assertEquals(1, c1.getMessageTracker().getGroupCount(group.getId()) assertEquals(1, c1.getMessageTracker().getGroupCount(group.getId())
@@ -518,13 +514,12 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum.getId(), contactId1From0, "Hi!", .sendInvitation(forum.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// invitee now shares same forum back // invitee now shares same forum back
forumSharingManager1.sendInvitation(forum.getId(), forumSharingManager1
contactId0From1, "I am re-sharing this forum with you.", .sendInvitation(forum.getId(), contactId0From1,
clock.currentTimeMillis()); "I am re-sharing this forum with you.");
// only now sync request message // only now sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -554,8 +549,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testContactRemoved() throws Exception { public void testContactRemoved() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
"Hi!", clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, "Hi!");
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -597,8 +592,7 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum.getId(), contactId1From0, "Hi!", .sendInvitation(forum.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -626,8 +620,7 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum.getId(), contactId1From0, "Hi!", .sendInvitation(forum.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -637,8 +630,7 @@ public class ForumSharingIntegrationTest
// second sharer sends invitation for same forum // second sharer sends invitation for same forum
assertNotNull(contactId1From2); assertNotNull(contactId1From2);
forumSharingManager2 forumSharingManager2
.sendInvitation(forum.getId(), contactId1From2, null, .sendInvitation(forum.getId(), contactId1From2, null);
clock.currentTimeMillis());
// sync second request message // sync second request message
sync2To1(1, true); sync2To1(1, true);
@@ -680,8 +672,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testSyncAfterReSharing() throws Exception { public void testSyncAfterReSharing() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
"Hi!", clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, "Hi!");
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -745,8 +737,7 @@ public class ForumSharingIntegrationTest
// send invitation again // send invitation again
forumSharingManager0 forumSharingManager0
.sendInvitation(forum.getId(), contactId1From0, "Hi!", .sendInvitation(forum.getId(), contactId1From0, "Hi!");
clock.currentTimeMillis());
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -787,8 +778,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testSessionResetAfterAbort() throws Exception { public void testSessionResetAfterAbort() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
"Hi!", clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, "Hi!");
// sync request message // sync request message
sync0To1(1, true); sync0To1(1, true);
@@ -836,8 +827,7 @@ public class ForumSharingIntegrationTest
// new invitation is possible now // new invitation is possible now
forumSharingManager0 forumSharingManager0
.sendInvitation(forum.getId(), contactId1From0, null, .sendInvitation(forum.getId(), contactId1From0, null);
clock.currentTimeMillis());
sync0To1(1, true); sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertRequestReceived(listener1, contactId0From1); assertRequestReceived(listener1, contactId0From1);
@@ -859,8 +849,8 @@ public class ForumSharingIntegrationTest
public void testDeletingAllMessagesWhenCompletingSession() public void testDeletingAllMessagesWhenCompletingSession()
throws Exception { throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
sync0To1(1, true); sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
@@ -909,8 +899,8 @@ public class ForumSharingIntegrationTest
sync1To0(1, true); sync1To0(1, true);
// sending invitation is possible again // sending invitation is possible again
forumSharingManager1.sendInvitation(forum.getId(), contactId0From1, forumSharingManager1
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId0From1, null);
sync1To0(1, true); sync1To0(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
@@ -944,8 +934,8 @@ public class ForumSharingIntegrationTest
public void testDeletingAllMessagesAfterDecline() public void testDeletingAllMessagesAfterDecline()
throws Exception { throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
sync0To1(1, true); sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
@@ -969,8 +959,8 @@ public class ForumSharingIntegrationTest
assertEquals(0, getMessages0From1().size()); assertEquals(0, getMessages0From1().size());
// re-sending invitation is possible // re-sending invitation is possible
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
sync0To1(1, true); sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
@@ -984,8 +974,8 @@ public class ForumSharingIntegrationTest
@Test @Test
public void testDeletingSomeMessages() throws Exception { public void testDeletingSomeMessages() throws Exception {
// send invitation // send invitation
forumSharingManager0.sendInvitation(forum.getId(), contactId1From0, forumSharingManager0
null, clock.currentTimeMillis()); .sendInvitation(forum.getId(), contactId1From0, null);
sync0To1(1, true); sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
@@ -996,7 +986,8 @@ public class ForumSharingIntegrationTest
Set<MessageId> toDelete = new HashSet<>(); Set<MessageId> toDelete = new HashSet<>();
toDelete.add(messageId); toDelete.add(messageId);
assertFalse(deleteMessages1From0(toDelete).allDeleted()); assertFalse(deleteMessages1From0(toDelete).allDeleted());
assertTrue(deleteMessages1From0(toDelete).hasInvitationSessionInProgress()); assertTrue(deleteMessages1From0(toDelete)
.hasInvitationSessionInProgress());
// decline invitation // decline invitation
respondToRequest(contactId0From1, true); respondToRequest(contactId0From1, true);
@@ -1006,9 +997,11 @@ public class ForumSharingIntegrationTest
// both can still not delete the invitation, // both can still not delete the invitation,
// because the response was not selected for deletion as well // because the response was not selected for deletion as well
assertFalse(deleteMessages1From0(toDelete).allDeleted()); assertFalse(deleteMessages1From0(toDelete).allDeleted());
assertTrue(deleteMessages1From0(toDelete).hasNotAllInvitationSelected()); assertTrue(
deleteMessages1From0(toDelete).hasNotAllInvitationSelected());
assertFalse(deleteMessages0From1(toDelete).allDeleted()); assertFalse(deleteMessages0From1(toDelete).allDeleted());
assertTrue(deleteMessages0From1(toDelete).hasNotAllInvitationSelected()); assertTrue(
deleteMessages0From1(toDelete).hasNotAllInvitationSelected());
// after selecting response, both messages can be deleted // after selecting response, both messages can be deleted
m0 = getMessages1From0(); m0 = getMessages1From0();
@@ -1023,8 +1016,10 @@ public class ForumSharingIntegrationTest
// 1 can still not delete the messages, as last one has not been ACKed // 1 can still not delete the messages, as last one has not been ACKed
assertFalse(deleteMessages0From1(toDelete).allDeleted()); assertFalse(deleteMessages0From1(toDelete).allDeleted());
assertFalse(deleteMessages0From1(toDelete).hasNotAllInvitationSelected()); assertFalse(
assertTrue(deleteMessages0From1(toDelete).hasInvitationSessionInProgress()); deleteMessages0From1(toDelete).hasNotAllInvitationSelected());
assertTrue(deleteMessages0From1(toDelete)
.hasInvitationSessionInProgress());
// 0 sends an ACK to their last message // 0 sends an ACK to their last message
sendAcks(c0, c1, contactId1From0, 1); sendAcks(c0, c1, contactId1From0, 1);