Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
b0928089ec Deliver test messages as though they arrived from contacts. 2020-11-27 11:30:40 +00:00
158 changed files with 1316 additions and 5019 deletions

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.api.client; package org.briarproject.bramble.api.client;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
@@ -120,17 +119,4 @@ public interface ClientHelper {
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap( Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
BdfDictionary properties) throws FormatException; BdfDictionary properties) throws FormatException;
/**
* Retrieves the contact ID from the group metadata of the given contact
* group.
*/
ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException, FormatException;
/**
* Stores the given contact ID in the group metadata of the given contact
* group.
*/
void setContactId(Transaction txn, GroupId contactGroupId, ContactId c)
throws DbException;
} }

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.client;
public interface ContactGroupConstants {
/**
* Group metadata key for associating a contact ID with a contact group.
*/
String GROUP_KEY_CONTACT_ID = "contactId";
}

View File

@@ -6,9 +6,7 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault @NotNullByDefault
public class ValidationUtils { public class ValidationUtils {
@@ -66,9 +64,4 @@ public class ValidationUtils {
if (dictionary != null && dictionary.size() != size) if (dictionary != null && dictionary.size() != size)
throw new FormatException(); throw new FormatException();
} }
public static void checkRange(@Nullable Long l, long min, long max)
throws FormatException {
if (l != null && (l < min || l > max)) throw new FormatException();
}
} }

View File

@@ -2,13 +2,11 @@ package org.briarproject.bramble.client;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader; import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory; import org.briarproject.bramble.api.data.BdfReaderFactory;
@@ -41,7 +39,6 @@ import java.util.Map.Entry;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
@@ -392,27 +389,4 @@ class ClientHelperImpl implements ClientHelper {
return tpMap; return tpMap;
} }
@Override
public ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException {
try {
BdfDictionary meta =
getGroupMetadataAsDictionary(txn, contactGroupId);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
}
@Override
public void setContactId(Transaction txn, GroupId contactGroupId,
ContactId c) throws DbException {
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, c.getInt()));
try {
mergeGroupMetadata(txn, contactGroupId, meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
} }

View File

@@ -5,5 +5,6 @@ interface ClientVersioningConstants {
// Metadata keys // Metadata keys
String MSG_KEY_UPDATE_VERSION = "version"; String MSG_KEY_UPDATE_VERSION = "version";
String MSG_KEY_LOCAL = "local"; String MSG_KEY_LOCAL = "local";
String GROUP_KEY_CONTACT_ID = "contactId";
} }

View File

@@ -50,6 +50,7 @@ import static java.util.Collections.emptyList;
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;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
@@ -160,7 +161,13 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
db.addGroup(txn, g); db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
clientHelper.setContactId(txn, g.getId(), c.getId()); BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
try {
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
// Create and store the first local update // Create and store the first local update
List<ClientVersion> versions = new ArrayList<>(clients); List<ClientVersion> versions = new ArrayList<>(clients);
Collections.sort(versions); Collections.sort(versions);
@@ -222,7 +229,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
Map<ClientMajorVersion, Visibility> after = Map<ClientMajorVersion, Visibility> after =
getVisibilities(newLocalStates, newRemoteStates); getVisibilities(newLocalStates, newRemoteStates);
// Call hooks for any visibilities that have changed // Call hooks for any visibilities that have changed
ContactId c = clientHelper.getContactId(txn, m.getGroupId()); ContactId c = getContactId(txn, m.getGroupId());
if (!before.equals(after)) { if (!before.equals(after)) {
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
callVisibilityHooks(txn, contact, before, after); callVisibilityHooks(txn, contact, before, after);
@@ -514,6 +521,17 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
storeUpdate(txn, g, states, 1); storeUpdate(txn, g, states, 1);
} }
private ContactId getContactId(Transaction txn, GroupId g)
throws DbException {
try {
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
} catch (FormatException e) {
throw new DbException(e);
}
}
private List<ClientState> updateStatesFromRemoteStates( private List<ClientState> updateStatesFromRemoteStates(
List<ClientState> oldLocalStates, List<ClientState> remoteStates) { List<ClientState> oldLocalStates, List<ClientState> remoteStates) {
Set<ClientMajorVersion> remoteSet = new HashSet<>(); Set<ClientMajorVersion> remoteSet = new HashSet<>();

View File

@@ -38,6 +38,7 @@ import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -59,6 +60,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
private final ClientId clientId = getClientId(); private final ClientId clientId = getClientId();
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
private ClientVersioningManagerImpl createInstance() { private ClientVersioningManagerImpl createInstance() {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -120,8 +123,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn, contactGroup);
oneOf(db).setGroupVisibility(txn, contact.getId(), oneOf(db).setGroupVisibility(txn, contact.getId(),
contactGroup.getId(), SHARED); contactGroup.getId(), SHARED);
oneOf(clientHelper).setContactId(txn, contactGroup.getId(), oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(),
contact.getId()); groupMeta);
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(clientHelper).createMessage(contactGroup.getId(), now, oneOf(clientHelper).createMessage(contactGroup.getId(), now,
@@ -457,8 +460,9 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
oneOf(db).deleteMessage(txn, oldRemoteUpdateId); oneOf(db).deleteMessage(txn, oldRemoteUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId); oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId);
// Get contact ID // Get contact ID
oneOf(clientHelper).getContactId(txn, contactGroup.getId()); oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
will(returnValue(contact.getId())); contactGroup.getId());
will(returnValue(groupMeta));
// No states or visibilities have changed // No states or visibilities have changed
}}); }});
@@ -488,9 +492,10 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
// Load the latest local update // Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId); oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody)); will(returnValue(oldLocalUpdateBody));
// Get contact ID // Get client ID
oneOf(clientHelper).getContactId(txn, contactGroup.getId()); oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
will(returnValue(contact.getId())); contactGroup.getId());
will(returnValue(groupMeta));
// No states or visibilities have changed // No states or visibilities have changed
}}); }});
@@ -541,6 +546,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
BdfDictionary newLocalUpdateMeta = BdfDictionary.of( BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L), new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
new BdfEntry(MSG_KEY_LOCAL, true)); new BdfEntry(MSG_KEY_LOCAL, true));
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper).toList(newRemoteUpdate); oneOf(clientHelper).toList(newRemoteUpdate);
@@ -570,8 +577,9 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate, oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
newLocalUpdateMeta, true, false); newLocalUpdateMeta, true, false);
// The client's visibility has changed // The client's visibility has changed
oneOf(clientHelper).getContactId(txn, contactGroup.getId()); oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
will(returnValue(contact.getId())); contactGroup.getId());
will(returnValue(groupMeta));
oneOf(db).getContact(txn, contact.getId()); oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact)); will(returnValue(contact));
oneOf(hook).onClientVisibilityChanging(txn, contact, visibility); oneOf(hook).onClientVisibilityChanging(txn, contact, visibility);
@@ -611,6 +619,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
BdfDictionary newLocalUpdateMeta = BdfDictionary.of( BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L), new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
new BdfEntry(MSG_KEY_LOCAL, true)); new BdfEntry(MSG_KEY_LOCAL, true));
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper).toList(newRemoteUpdate); oneOf(clientHelper).toList(newRemoteUpdate);
@@ -640,8 +650,9 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate, oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
newLocalUpdateMeta, true, false); newLocalUpdateMeta, true, false);
// The client's visibility has changed // The client's visibility has changed
oneOf(clientHelper).getContactId(txn, contactGroup.getId()); oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
will(returnValue(contact.getId())); contactGroup.getId());
will(returnValue(groupMeta));
oneOf(db).getContact(txn, contact.getId()); oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact)); will(returnValue(contact));
oneOf(hook).onClientVisibilityChanging(txn, contact, INVISIBLE); oneOf(hook).onClientVisibilityChanging(txn, contact, INVISIBLE);

View File

@@ -141,16 +141,6 @@
android:value="org.briarproject.briar.android.conversation.ConversationActivity" /> android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
</activity> </activity>
<activity
android:name=".android.conversation.ConversationSettingsActivity"
android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity"
android:label="@string/disappearing_messages_title"
android:theme="@style/BriarTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
</activity>
<activity <activity
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity" android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title" android:label="@string/groups_create_group_title"

View File

@@ -14,7 +14,6 @@ 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;
@@ -40,7 +39,6 @@ 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;
@@ -171,10 +169,6 @@ 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

@@ -28,8 +28,6 @@ import org.briarproject.briar.android.contact.add.remote.NicknameFragment;
import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity; import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity;
import org.briarproject.briar.android.conversation.AliasDialogFragment; import org.briarproject.briar.android.conversation.AliasDialogFragment;
import org.briarproject.briar.android.conversation.ConversationActivity; import org.briarproject.briar.android.conversation.ConversationActivity;
import org.briarproject.briar.android.conversation.ConversationSettingsActivity;
import org.briarproject.briar.android.conversation.ConversationSettingsFragment;
import org.briarproject.briar.android.conversation.ImageActivity; import org.briarproject.briar.android.conversation.ImageActivity;
import org.briarproject.briar.android.conversation.ImageFragment; import org.briarproject.briar.android.conversation.ImageFragment;
import org.briarproject.briar.android.forum.CreateForumActivity; import org.briarproject.briar.android.forum.CreateForumActivity;
@@ -186,8 +184,6 @@ public interface ActivityComponent {
void inject(PendingContactListActivity activity); void inject(PendingContactListActivity activity);
void inject(ConversationSettingsActivity activity);
// Fragments // Fragments
void inject(AuthorNameFragment fragment); void inject(AuthorNameFragment fragment);
@@ -238,6 +234,4 @@ public interface ActivityComponent {
void inject(ImageFragment imageFragment); void inject(ImageFragment imageFragment);
void inject(ConversationSettingsFragment conversationSettingsFragment);
} }

View File

@@ -140,8 +140,6 @@ import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
import static org.briarproject.briar.android.util.UiUtils.observeOnce; import static org.briarproject.briar.android.util.UiUtils.observeOnce;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES_AUTO_DELETE;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_ONLY;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -275,11 +273,15 @@ public class ConversationActivity extends BriarActivity
ImagePreview imagePreview = findViewById(R.id.imagePreview); ImagePreview imagePreview = findViewById(R.id.imagePreview);
sendController = new TextAttachmentController(textInputView, sendController = new TextAttachmentController(textInputView,
imagePreview, this, viewModel); imagePreview, this, viewModel);
observeOnce(viewModel.getPrivateMessageFormat(), this, format -> { viewModel.hasImageSupport().observe(this, new Observer<Boolean>() {
if (format != TEXT_ONLY) { @Override
// TODO: remove cast when removing feature flag public void onChanged(@Nullable Boolean hasSupport) {
((TextAttachmentController) sendController) if (hasSupport != null && hasSupport) {
.setImagesSupported(); // TODO: remove cast when removing feature flag
((TextAttachmentController) sendController)
.setImagesSupported();
viewModel.hasImageSupport().removeObserver(this);
}
} }
}); });
} else { } else {
@@ -382,12 +384,6 @@ public class ConversationActivity extends BriarActivity
// enable alias action if available // enable alias action if available
observeOnce(viewModel.getContact(), this, contact -> observeOnce(viewModel.getContact(), this, contact ->
menu.findItem(R.id.action_set_alias).setEnabled(true)); menu.findItem(R.id.action_set_alias).setEnabled(true));
// show auto-delete timer setting only, if contacts supports it
observeOnce(viewModel.getPrivateMessageFormat(), this, format -> {
boolean visible = format == TEXT_IMAGES_AUTO_DELETE;
menu.findItem(R.id.action_conversation_settings)
.setVisible(visible);
});
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@@ -409,12 +405,6 @@ public class ConversationActivity extends BriarActivity
AliasDialogFragment.newInstance().show( AliasDialogFragment.newInstance().show(
getSupportFragmentManager(), AliasDialogFragment.TAG); getSupportFragmentManager(), AliasDialogFragment.TAG);
return true; return true;
case R.id.action_conversation_settings:
if (contactId == null) return false;
intent = new Intent(this, ConversationSettingsActivity.class);
intent.putExtra(CONTACT_ID, contactId.getInt());
startActivity(intent);
return true;
case R.id.action_delete_all_messages: case R.id.action_delete_all_messages:
askToDeleteAllMessages(); askToDeleteAllMessages();
return true; return true;
@@ -667,8 +657,8 @@ public class ConversationActivity extends BriarActivity
supportFinishAfterTransition(); supportFinishAfterTransition();
} }
} else if (e instanceof ConversationMessageReceivedEvent) { } else if (e instanceof ConversationMessageReceivedEvent) {
ConversationMessageReceivedEvent<?> p = ConversationMessageReceivedEvent p =
(ConversationMessageReceivedEvent<?>) e; (ConversationMessageReceivedEvent) e;
if (p.getContactId().equals(contactId)) { if (p.getContactId().equals(contactId)) {
LOG.info("Message received, adding"); LOG.info("Message received, adding");
onNewConversationMessage(p.getMessageHeader()); onNewConversationMessage(p.getMessageHeader());
@@ -766,10 +756,18 @@ 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();
viewModel.sendMessage(text, attachmentHeaders); long timestamp = System.currentTimeMillis();
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));
@@ -974,11 +972,13 @@ 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); accept, timestamp);
break; break;
case FORUM: case FORUM:
respondToForumRequest(item.getSessionId(), accept); respondToForumRequest(item.getSessionId(), accept);
@@ -1054,8 +1054,9 @@ public class ConversationActivity extends BriarActivity
@DatabaseExecutor @DatabaseExecutor
private void respondToIntroductionRequest(SessionId sessionId, private void respondToIntroductionRequest(SessionId sessionId,
boolean accept) throws DbException { boolean accept, long time) throws DbException {
introductionManager.respondToIntroduction(contactId, sessionId, accept); introductionManager.respondToIntroduction(contactId, sessionId, time,
accept);
} }
@DatabaseExecutor @DatabaseExecutor

View File

@@ -22,7 +22,7 @@ abstract class ConversationItem {
protected String text; protected String text;
private final MessageId id; private final MessageId id;
private final GroupId groupId; private final GroupId groupId;
private final long time, autoDeleteTimer; private final long time;
private final boolean isIncoming; private final boolean isIncoming;
private boolean read, sent, seen; private boolean read, sent, seen;
@@ -32,7 +32,6 @@ abstract class ConversationItem {
this.id = h.getId(); this.id = h.getId();
this.groupId = h.getGroupId(); this.groupId = h.getGroupId();
this.time = h.getTimestamp(); this.time = h.getTimestamp();
this.autoDeleteTimer = h.getAutoDeleteTimer();
this.read = h.isRead(); this.read = h.isRead();
this.sent = h.isSent(); this.sent = h.isSent();
this.seen = h.isSeen(); this.seen = h.isSeen();
@@ -69,10 +68,6 @@ abstract class ConversationItem {
return time; return time;
} }
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
/** /**
* Only useful for incoming messages. * Only useful for incoming messages.
*/ */

View File

@@ -12,11 +12,8 @@ import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.bramble.util.StringUtils.trim; import static org.briarproject.bramble.util.StringUtils.trim;
import static org.briarproject.briar.android.util.UiUtils.formatDate; import static org.briarproject.briar.android.util.UiUtils.formatDate;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -29,7 +26,6 @@ abstract class ConversationItemViewHolder extends ViewHolder {
private final OutItemViewHolder outViewHolder; private final OutItemViewHolder outViewHolder;
private final TextView text; private final TextView text;
protected final TextView time; protected final TextView time;
private final View bomb;
@Nullable @Nullable
private String itemKey = null; private String itemKey = null;
@@ -42,7 +38,6 @@ abstract class ConversationItemViewHolder extends ViewHolder {
layout = v.findViewById(R.id.layout); layout = v.findViewById(R.id.layout);
text = v.findViewById(R.id.text); text = v.findViewById(R.id.text);
time = v.findViewById(R.id.time); time = v.findViewById(R.id.time);
bomb = v.findViewById(R.id.bomb);
} }
@CallSuper @CallSuper
@@ -57,9 +52,6 @@ abstract class ConversationItemViewHolder extends ViewHolder {
long timestamp = item.getTime(); long timestamp = item.getTime();
time.setText(formatDate(time.getContext(), timestamp)); time.setText(formatDate(time.getContext(), timestamp));
boolean showBomb = item.getAutoDeleteTimer() != NO_AUTO_DELETE_TIMER;
bomb.setVisibility(showBomb ? VISIBLE : GONE);
if (outViewHolder != null) outViewHolder.bind(item); if (outViewHolder != null) outViewHolder.bind(item);
} }

View File

@@ -1,80 +0,0 @@
package org.briarproject.briar.android.conversation;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.appcompat.app.ActionBar;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ConversationSettingsActivity extends BriarActivity implements
BaseFragmentListener {
@Inject
ViewModelProvider.Factory viewModelFactory;
private ConversationViewModel viewModel;
private ContactId contactId;
@Override
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
Intent i = getIntent();
int id = i.getIntExtra(CONTACT_ID, -1);
if (id == -1) throw new IllegalStateException();
contactId = new ContactId(id);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
setContentView(R.layout.activity_conversation_settings);
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ConversationViewModel.class);
}
@Override
public void onResume() {
super.onResume();
// Trigger loading of contact data, noop if data was loaded already.
//
// We can only start loading data *after* we are sure
// the user has signed in. After sign-in, onCreate() isn't run again.
if (signedIn()) viewModel.setContactId(contactId);
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return false;
}
}

View File

@@ -1,158 +0,0 @@
package org.briarproject.briar.android.conversation;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.appcompat.widget.SwitchCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ConversationSettingsFragment extends BaseFragment {
public static final String TAG =
ConversationSettingsFragment.class.getName();
private static final Logger LOG =
Logger.getLogger(ConversationSettingsFragment.class.getName());
@Inject
ViewModelProvider.Factory viewModelFactory;
@Inject
@DatabaseExecutor
Executor dbExecutor;
@Inject
TransactionManager db;
@Inject
AutoDeleteManager autoDeleteManager;
private ConversationSettingsActivity listener;
private ConversationViewModel viewModel;
private SwitchCompat switchDisappearingMessages;
private volatile boolean disappearingMessages = false;
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (ConversationSettingsActivity) context;
listener.getActivityComponent().inject(this);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View contentView =
inflater.inflate(R.layout.fragment_conversation_settings,
container, false);
switchDisappearingMessages =
contentView.findViewById(R.id.switchDisappearingMessages);
switchDisappearingMessages
.setOnCheckedChangeListener((button, value) -> viewModel
.setAutoDeleteTimerEnabled(value));
TextView buttonLearnMore =
contentView.findViewById(R.id.buttonLearnMore);
buttonLearnMore.setOnClickListener(e -> showLearnMoreDialog());
viewModel = ViewModelProviders.of(requireActivity(), viewModelFactory)
.get(ConversationViewModel.class);
return contentView;
}
@Override
public void onStart() {
super.onStart();
switchDisappearingMessages.setEnabled(false);
loadSettings();
}
private void loadSettings() {
observeOnce(viewModel.getContact(), this, c -> {
dbExecutor.execute(() -> {
try {
db.transaction(false, txn -> {
long timer = autoDeleteManager
.getAutoDeleteTimer(txn, c.getId());
disappearingMessages = timer != NO_AUTO_DELETE_TIMER;
});
listener.runOnUiThreadUnlessDestroyed(
this::displaySettings);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
});
}
private void displaySettings() {
switchDisappearingMessages.setChecked(disappearingMessages);
switchDisappearingMessages.setEnabled(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.help_action, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_help) {
showLearnMoreDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showLearnMoreDialog() {
ConversationSettingsLearnMoreDialog
dialog = new ConversationSettingsLearnMoreDialog();
dialog.show(requireActivity().getSupportFragmentManager(),
ConversationSettingsLearnMoreDialog.TAG);
}
}

View File

@@ -1,44 +0,0 @@
package org.briarproject.briar.android.conversation;
import android.app.Dialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.briar.R;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
@MethodsNotNullByDefault
public class ConversationSettingsLearnMoreDialog extends DialogFragment {
final static String TAG =
ConversationSettingsLearnMoreDialog.class.getName();
static ConversationSettingsLearnMoreDialog newInstance() {
return new ConversationSettingsLearnMoreDialog();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
FragmentActivity activity = requireActivity();
AlertDialog.Builder builder =
new AlertDialog.Builder(activity, R.style.BriarDialogTheme);
LayoutInflater inflater = LayoutInflater.from(getContext());
View view = inflater.inflate(
R.layout.fragment_conversation_settings_learn_more, null);
builder.setView(view);
builder.setTitle(R.string.disappearing_messages_title);
builder.setNeutralButton(R.string.ok, null);
return builder.create();
}
}

View File

@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.contact.ContactManager;
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.db.TransactionManager;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -29,13 +28,10 @@ import org.briarproject.briar.android.attachment.AttachmentRetriever;
import org.briarproject.briar.android.util.UiUtils; 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.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;
import org.briarproject.briar.api.messaging.PrivateMessageFactory; import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.api.messaging.PrivateMessageFormat;
import org.briarproject.briar.api.messaging.PrivateMessageHeader; import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent; import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
@@ -54,7 +50,6 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations; import androidx.lifecycle.Transformations;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
@@ -62,15 +57,12 @@ import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce; import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_ONLY;
@NotNullByDefault @NotNullByDefault
public class ConversationViewModel extends AndroidViewModel public class ConversationViewModel extends AndroidViewModel
implements EventListener, AttachmentManager { implements EventListener, AttachmentManager {
private static final Logger LOG = private static Logger LOG =
getLogger(ConversationViewModel.class.getName()); getLogger(ConversationViewModel.class.getName());
private static final String SHOW_ONBOARDING_IMAGE = private static final String SHOW_ONBOARDING_IMAGE =
@@ -88,8 +80,6 @@ public class ConversationViewModel extends AndroidViewModel
private final PrivateMessageFactory privateMessageFactory; private final PrivateMessageFactory privateMessageFactory;
private final AttachmentRetriever attachmentRetriever; private final AttachmentRetriever attachmentRetriever;
private final AttachmentCreator attachmentCreator; private final AttachmentCreator attachmentCreator;
private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
@Nullable @Nullable
private ContactId contactId = null; private ContactId contactId = null;
@@ -99,7 +89,7 @@ public class ConversationViewModel extends AndroidViewModel
private final LiveData<String> contactName = private final LiveData<String> contactName =
Transformations.map(contact, UiUtils::getContactDisplayName); Transformations.map(contact, UiUtils::getContactDisplayName);
private final LiveData<GroupId> messagingGroupId; private final LiveData<GroupId> messagingGroupId;
private final MutableLiveData<PrivateMessageFormat> privateMessageFormat = private final MutableLiveData<Boolean> imageSupport =
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveEvent<Boolean> showImageOnboarding = private final MutableLiveEvent<Boolean> showImageOnboarding =
new MutableLiveEvent<>(); new MutableLiveEvent<>();
@@ -122,9 +112,7 @@ public class ConversationViewModel extends AndroidViewModel
SettingsManager settingsManager, SettingsManager settingsManager,
PrivateMessageFactory privateMessageFactory, PrivateMessageFactory privateMessageFactory,
AttachmentRetriever attachmentRetriever, AttachmentRetriever attachmentRetriever,
AttachmentCreator attachmentCreator, AttachmentCreator attachmentCreator) {
AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager) {
super(application); super(application);
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.db = db; this.db = db;
@@ -135,8 +123,6 @@ public class ConversationViewModel extends AndroidViewModel
this.privateMessageFactory = privateMessageFactory; this.privateMessageFactory = privateMessageFactory;
this.attachmentRetriever = attachmentRetriever; this.attachmentRetriever = attachmentRetriever;
this.attachmentCreator = attachmentCreator; this.attachmentCreator = attachmentCreator;
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);
@@ -219,13 +205,16 @@ public class ConversationViewModel extends AndroidViewModel
} }
@UiThread @UiThread
void sendMessage(@Nullable String text, List<AttachmentHeader> headers) { void sendMessage(@Nullable String text,
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(imageSupport, hasImageSupport -> {
storeMessage(requireNonNull(contactId), groupId, text, requireNonNull(hasImageSupport);
headers, format)); createMessage(groupId, text, headers, timestamp,
hasImageSupport);
});
}); });
} }
@@ -255,10 +244,10 @@ public class ConversationViewModel extends AndroidViewModel
@DatabaseExecutor @DatabaseExecutor
private void checkFeaturesAndOnboarding(ContactId c) throws DbException { private void checkFeaturesAndOnboarding(ContactId c) throws DbException {
// check if images and auto-deletion are supported // check if images are supported
PrivateMessageFormat format = db.transactionWithResult(true, txn -> boolean imagesSupported = db.transactionWithResult(true, txn ->
messagingManager.getContactMessageFormat(txn, c)); messagingManager.contactSupportsImages(txn, c));
privateMessageFormat.postValue(format); imageSupport.postValue(imagesSupported);
// check if introductions are supported // check if introductions are supported
Collection<Contact> contacts = contactManager.getContacts(); Collection<Contact> contacts = contactManager.getContacts();
@@ -267,7 +256,7 @@ public class ConversationViewModel extends AndroidViewModel
// we only show one onboarding dialog at a time // we only show one onboarding dialog at a time
Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE); Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
if (format != TEXT_ONLY && if (imagesSupported &&
settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) { settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) {
onOnboardingShown(SHOW_ONBOARDING_IMAGE); onOnboardingShown(SHOW_ONBOARDING_IMAGE);
showImageOnboarding.postEvent(true); showImageOnboarding.postEvent(true);
@@ -285,67 +274,40 @@ public class ConversationViewModel extends AndroidViewModel
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE); settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
} }
private PrivateMessage createMessage(Transaction txn, ContactId c, @UiThread
GroupId groupId, @Nullable String text, private void createMessage(GroupId groupId, @Nullable String text,
List<AttachmentHeader> headers, PrivateMessageFormat format) List<AttachmentHeader> headers, long timestamp,
throws DbException { boolean hasImageSupport) {
long timestamp =
conversationManager.getTimestampForOutgoingMessage(txn, c);
try { try {
if (format == TEXT_ONLY) { PrivateMessage pm;
return privateMessageFactory.createLegacyPrivateMessage( if (hasImageSupport) {
groupId, timestamp, requireNonNull(text)); pm = privateMessageFactory.createPrivateMessage(groupId,
} else if (format == TEXT_IMAGES) {
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers); timestamp, text, headers);
} else { } else {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c, pm = privateMessageFactory.createLegacyPrivateMessage(
timestamp); groupId, timestamp, requireNonNull(text));
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers, timer);
} }
storeMessage(pm);
} catch (FormatException e) { } catch (FormatException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }
@UiThread @UiThread
private void storeMessage(ContactId c, GroupId groupId, private void storeMessage(PrivateMessage m) {
@Nullable String text, List<AttachmentHeader> headers, attachmentCreator.onAttachmentsSent(m.getMessage().getId());
PrivateMessageFormat format) {
dbExecutor.execute(() -> { dbExecutor.execute(() -> {
try { try {
db.transaction(false, txn -> { long start = now();
long start = now(); messagingManager.addLocalMessage(m);
PrivateMessage m = createMessage(txn, c, groupId, text, logDuration(LOG, "Storing message", start);
headers, format); Message message = m.getMessage();
messagingManager.addLocalMessage(txn, m); PrivateMessageHeader h = new PrivateMessageHeader(
logDuration(LOG, "Storing message", start); message.getId(), message.getGroupId(),
Message message = m.getMessage(); message.getTimestamp(), true, true, false, false,
PrivateMessageHeader h = new PrivateMessageHeader( m.hasText(), m.getAttachmentHeaders());
message.getId(), message.getGroupId(), // TODO add text to cache when available here
message.getTimestamp(), true, true, false, false, addedHeader.postEvent(h);
m.hasText(), m.getAttachmentHeaders(),
m.getAutoDeleteTimer());
// TODO add text to cache when available here
MessageId id = message.getId();
txn.attach(() -> attachmentCreator.onAttachmentsSent(id));
addedHeader.postEvent(h);
});
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
void setAutoDeleteTimerEnabled(boolean enabled) {
final long timer = enabled ? DAYS.toMillis(7) : NO_AUTO_DELETE_TIMER;
// ContactId is set before menu gets inflated and UI interaction
final ContactId c = requireNonNull(contactId);
dbExecutor.execute(() -> {
try {
db.transaction(false, txn ->
autoDeleteManager.setAutoDeleteTimer(txn, c, timer));
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }
@@ -368,8 +330,8 @@ public class ConversationViewModel extends AndroidViewModel
return contactName; return contactName;
} }
LiveData<PrivateMessageFormat> getPrivateMessageFormat() { LiveData<Boolean> hasImageSupport() {
return privateMessageFormat; return imageSupport;
} }
LiveEvent<Boolean> showImageOnboarding() { LiveEvent<Boolean> showImageOnboarding() {

View File

@@ -211,7 +211,8 @@ public class IntroductionMessageFragment extends BaseFragment
introductionActivity.runOnDbThread(() -> { introductionActivity.runOnDbThread(() -> {
// actually make the introduction // actually make the introduction
try { try {
introductionManager.makeIntroduction(c1, c2, text); long timestamp = System.currentTimeMillis();
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,8 +7,6 @@ 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;
@@ -17,8 +15,6 @@ 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;
@@ -39,8 +35,6 @@ 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
@@ -49,12 +43,9 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
implements CreateGroupController { implements CreateGroupController {
private static final Logger LOG = private static final Logger LOG =
getLogger(CreateGroupControllerImpl.class.getName()); Logger.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;
@@ -65,26 +56,16 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
private final Clock clock; private final Clock clock;
@Inject @Inject
CreateGroupControllerImpl( CreateGroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
@DatabaseExecutor Executor dbExecutor,
@CryptoExecutor Executor cryptoExecutor, @CryptoExecutor Executor cryptoExecutor,
TransactionManager db, LifecycleManager lifecycleManager, ContactManager contactManager,
AutoDeleteManager autoDeleteManager, IdentityManager identityManager, PrivateGroupFactory groupFactory,
ConversationManager conversationManager,
LifecycleManager lifecycleManager,
ContactManager contactManager,
IdentityManager identityManager,
PrivateGroupFactory groupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
PrivateGroupManager groupManager, PrivateGroupManager groupManager,
GroupInvitationFactory groupInvitationFactory, GroupInvitationFactory groupInvitationFactory,
GroupInvitationManager groupInvitationManager, GroupInvitationManager groupInvitationManager, Clock clock) {
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;
@@ -148,14 +129,16 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
ResultExceptionHandler<Void, DbException> handler) { ResultExceptionHandler<Void, DbException> handler) {
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
db.transaction(true, txn -> { LocalAuthor localAuthor = identityManager.getLocalAuthor();
LocalAuthor localAuthor = List<Contact> contacts = new ArrayList<>();
identityManager.getLocalAuthor(txn); for (ContactId c : contactIds) {
List<InvitationContext> contexts = try {
createInvitationContexts(txn, contactIds); contacts.add(contactManager.getContact(c));
txn.attach(() -> signInvitations(g, localAuthor, contexts, } catch (NoSuchContactException e) {
text, handler)); // Continue
}); }
}
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);
@@ -163,32 +146,17 @@ 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,
timestamp);
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,
List<InvitationContext> contexts, @Nullable String text, Collection<Contact> contacts, @Nullable String text,
ResultExceptionHandler<Void, DbException> handler) { ResultExceptionHandler<Void, DbException> handler) {
cryptoExecutor.execute(() -> { cryptoExecutor.execute(() -> {
for (InvitationContext ctx : contexts) { long timestamp = clock.currentTimeMillis();
ctx.signature = groupInvitationFactory.signInvitation( List<InvitationContext> contexts = new ArrayList<>();
ctx.contact, g, ctx.timestamp, for (Contact c : contacts) {
localAuthor.getPrivateKey()); byte[] signature = groupInvitationFactory.signInvitation(c, g,
timestamp, localAuthor.getPrivateKey());
contexts.add(new InvitationContext(c.getId(), timestamp,
signature));
} }
sendInvitations(g, contexts, text, handler); sendInvitations(g, contexts, text, handler);
}); });
@@ -199,16 +167,16 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
ResultExceptionHandler<Void, DbException> handler) { ResultExceptionHandler<Void, DbException> handler) {
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
for (InvitationContext ctx : contexts) { for (InvitationContext context : contexts) {
try { try {
groupInvitationManager.sendInvitation(g, groupInvitationManager.sendInvitation(g,
ctx.contact.getId(), text, ctx.timestamp, context.contactId, text, context.timestamp,
requireNonNull(ctx.signature), context.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);
@@ -219,16 +187,15 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
private static class InvitationContext { private static class InvitationContext {
private final Contact contact; private final ContactId contactId;
private final long timestamp, autoDeleteTimer; private final long timestamp;
@Nullable private final byte[] signature;
private byte[] signature = null;
private InvitationContext(Contact contact, long timestamp, private InvitationContext(ContactId contactId, long timestamp,
long autoDeleteTimer) { byte[] signature) {
this.contact = contact; this.contactId = contactId;
this.timestamp = timestamp; this.timestamp = timestamp;
this.autoDeleteTimer = autoDeleteTimer; this.signature = signature;
} }
} }
} }

View File

@@ -10,9 +10,11 @@ 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;
@@ -23,7 +25,6 @@ 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
@@ -32,16 +33,21 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
implements ShareBlogController { implements ShareBlogController {
private final static Logger LOG = private final static Logger LOG =
getLogger(ShareBlogControllerImpl.class.getName()); Logger.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,
BlogSharingManager blogSharingManager) { ConversationManager conversationManager,
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
@@ -56,7 +62,10 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
try { try {
for (ContactId c : contacts) { for (ContactId c : contacts) {
try { try {
blogSharingManager.sendInvitation(g, c, text); long time = Math.max(clock.currentTimeMillis(),
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,8 +10,10 @@ 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;
@@ -23,7 +25,6 @@ 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
@@ -32,16 +33,21 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
implements ShareForumController { implements ShareForumController {
private final static Logger LOG = private final static Logger LOG =
getLogger(ShareForumControllerImpl.class.getName()); Logger.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,
ForumSharingManager forumSharingManager) { ConversationManager conversationManager,
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
@@ -56,7 +62,10 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
try { try {
for (ContactId c : contacts) { for (ContactId c : contacts) {
try { try {
forumSharingManager.sendInvitation(g, c, text); long time = Math.max(clock.currentTimeMillis(),
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

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M11.25,6A3.25,3.25 0 0,1 14.5,2.75A3.25,3.25 0 0,1 17.75,6C17.75,6.42 18.08,6.75 18.5,6.75C18.92,6.75 19.25,6.42 19.25,6V5.25H20.75V6A2.25,2.25 0 0,1 18.5,8.25A2.25,2.25 0 0,1 16.25,6A1.75,1.75 0 0,0 14.5,4.25A1.75,1.75 0 0,0 12.75,6H14V7.29C16.89,8.15 19,10.83 19,14A7,7 0 0,1 12,21A7,7 0 0,1 5,14C5,10.83 7.11,8.15 10,7.29V6H11.25M22,6H24V7H22V6M19,4V2H20V4H19M20.91,4.38L22.33,2.96L23.04,3.67L21.62,5.09L20.91,4.38Z" />
</vector>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment"
android:name="org.briarproject.briar.android.conversation.ConversationSettingsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="@dimen/margin_large">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageViewBomb"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="16dp"
android:src="@drawable/ic_bomb"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/colorControlNormal" />
<TextView
android:id="@+id/buttonLearnMore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:clickable="true"
android:focusable="true"
android:text="@string/learn_more"
android:textColor="?android:attr/textColorLink"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageViewBomb" />
<View
android:id="@+id/divider"
style="@style/Divider.Horizontal"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="16dp"
android:background="?android:attr/listDivider"
app:layout_constraintBottom_toTopOf="@+id/barrier1"
app:layout_constraintTop_toBottomOf="@+id/buttonLearnMore" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top" />
<TextView
android:id="@+id/headlineSetting"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/disappearing_messages_title"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barrier1" />
<TextView
android:id="@+id/textSetting"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/disappearing_messages_summary"
app:layout_constraintBottom_toTopOf="@id/barrier2"
app:layout_constraintEnd_toStartOf="@id/switchDisappearingMessages"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/headlineSetting" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switchDisappearingMessages"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/barrier2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textSetting"
app:layout_constraintTop_toBottomOf="@+id/barrier1" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="?dialogPreferredPadding">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/disappearing_messages_explanation_long"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ScrollView>

View File

@@ -68,16 +68,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Dec 24, 13:37" /> tools:text="Dec 24, 13:37" />
<ImageView
android:id="@+id/bomb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:src="@drawable/ic_bomb"
app:tint="?android:attr/textColorSecondary"
tools:ignore="ContentDescription" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -67,16 +67,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Dec 24, 13:37" /> tools:text="Dec 24, 13:37" />
<ImageView
android:id="@+id/bomb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:src="@drawable/ic_bomb"
app:tint="?android:attr/textColorSecondary"
tools:ignore="ContentDescription" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -68,17 +68,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Dec 24, 13:37" /> tools:text="Dec 24, 13:37" />
<ImageView
android:id="@+id/bomb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:src="@drawable/ic_bomb"
app:tint="?android:attr/textColorSecondary"
tools:ignore="ContentDescription"
tools:visibility="visible" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -73,20 +73,11 @@
style="@style/TextMessage.Timestamp" style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="4dp" android:layout_marginEnd="6dp"
android:layout_marginRight="4dp" android:layout_marginRight="6dp"
android:textColor="@color/private_message_date_inverse" android:textColor="@color/private_message_date_inverse"
tools:text="Dec 24, 13:37" /> tools:text="Dec 24, 13:37" />
<ImageView
android:id="@+id/bomb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:src="@drawable/ic_bomb"
tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/status" android:id="@+id/status"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -48,28 +48,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/message_bubble_timestamp_margin" android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
app:layout_constraintEnd_toStartOf="@+id/bomb" app:layout_constraintEnd_toEndOf="@+id/text"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text" app:layout_constraintTop_toBottomOf="@+id/text"
tools:text="Dec 24, 13:37" /> tools:text="Dec 24, 13:37" />
<ImageView
android:id="@+id/bomb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
android:src="@drawable/ic_bomb"
app:layout_constraintEnd_toEndOf="@+id/text"
app:layout_constraintStart_toEndOf="@+id/time"
app:layout_constraintTop_toBottomOf="@+id/text"
app:tint="?android:attr/textColorSecondary"
tools:ignore="ContentDescription"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>

View File

@@ -55,26 +55,14 @@
app:layout_constraintTop_toBottomOf="@+id/text" app:layout_constraintTop_toBottomOf="@+id/text"
tools:text="Dec 24, 13:37" /> tools:text="Dec 24, 13:37" />
<ImageView
android:id="@+id/bomb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:src="@drawable/ic_bomb"
app:layout_constraintBottom_toBottomOf="@+id/time"
app:layout_constraintStart_toEndOf="@+id/time"
app:layout_constraintTop_toTopOf="@+id/time"
tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/status" android:id="@+id/status"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="4dp" android:layout_marginStart="@dimen/margin_medium"
android:layout_marginLeft="4dp" android:layout_marginLeft="@dimen/margin_medium"
app:layout_constraintBottom_toBottomOf="@+id/time" app:layout_constraintBottom_toBottomOf="@+id/time"
app:layout_constraintStart_toEndOf="@+id/bomb" app:layout_constraintStart_toEndOf="@+id/time"
app:layout_constraintTop_toTopOf="@+id/time" app:layout_constraintTop_toTopOf="@+id/time"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
tools:src="@drawable/message_delivered" /> tools:src="@drawable/message_delivered" />

View File

@@ -68,27 +68,10 @@
style="@style/TextMessage.Timestamp" style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@+id/bomb" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/acceptButton" app:layout_constraintTop_toBottomOf="@+id/acceptButton"
tools:text="Dec 24, 13:37" /> tools:text="Dec 24, 13:37" />
<ImageView
android:id="@+id/bomb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:src="@drawable/ic_bomb"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/time"
app:layout_constraintTop_toBottomOf="@+id/acceptButton"
app:tint="?android:attr/textColorSecondary"
tools:ignore="ContentDescription"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>

View File

@@ -1,37 +1,30 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_introduction" android:id="@+id/action_introduction"
android:enabled="false"
android:icon="@drawable/introduction_white" android:icon="@drawable/introduction_white"
android:title="@string/introduction_menu_item" android:title="@string/introduction_menu_item"
app:showAsAction="never" /> android:enabled="false"
app:showAsAction="never"/>
<item <item
android:id="@+id/action_set_alias" android:id="@+id/action_set_alias"
android:enabled="false"
android:title="@string/set_contact_alias" android:title="@string/set_contact_alias"
app:showAsAction="never" /> android:enabled="false"
app:showAsAction="never"/>
<item
android:id="@+id/action_conversation_settings"
android:title="@string/menu_item_disappearing_messages"
android:visible="false"
app:showAsAction="never"
tools:visible="true" />
<item <item
android:id="@+id/action_delete_all_messages" android:id="@+id/action_delete_all_messages"
android:title="@string/delete_all_messages" android:title="@string/delete_all_messages"
app:showAsAction="never" /> app:showAsAction="never"/>
<item <item
android:id="@+id/action_social_remove_person" android:id="@+id/action_social_remove_person"
android:icon="@drawable/action_delete_white" android:icon="@drawable/action_delete_white"
android:title="@string/delete_contact" android:title="@string/delete_contact"
app:showAsAction="never" /> app:showAsAction="never"/>
</menu> </menu>

View File

@@ -165,7 +165,6 @@
<string name="set_contact_alias">Change contact name</string> <string name="set_contact_alias">Change contact name</string>
<string name="set_contact_alias_hint">Contact name</string> <string name="set_contact_alias_hint">Contact name</string>
<string name="set_alias_button">Change</string> <string name="set_alias_button">Change</string>
<string name="menu_item_disappearing_messages">Disappearing messages</string>
<string name="delete_all_messages">Delete all messages</string> <string name="delete_all_messages">Delete all messages</string>
<string name="dialog_title_delete_all_messages">Confirm Message Deletion</string> <string name="dialog_title_delete_all_messages">Confirm Message Deletion</string>
<string name="dialog_message_delete_all_messages">Are you sure that you want to delete all messages?</string> <string name="dialog_message_delete_all_messages">Are you sure that you want to delete all messages?</string>
@@ -546,20 +545,6 @@
<string name="choose_ringtone_title">Choose ringtone</string> <string name="choose_ringtone_title">Choose ringtone</string>
<string name="cannot_load_ringtone">Cannot load ringtone</string> <string name="cannot_load_ringtone">Cannot load ringtone</string>
<!-- Conversation Settings -->
<string name="disappearing_messages_title">Disappearing messages</string>
<string name="disappearing_messages_cat_title">Self-destructing messages</string>
<string name="disappearing_messages_explanation_long">You can configure disappearing messages here.
Messages that will disappear are marked with a bomb icon.
\n\nTurning on this setting will make new messages in this conversation automatically disappear 7 days after being received.
This applies to messages you send to your contact as well as messages your contact sends to you.
Your contact can also change this setting for the both of you.
\n\nKeep in mind that recipients can still make analogue copies of the messages you sent.
\n\nIf you change this setting, it will apply to your messages immediately and to messages of
your contact once they receive any message from you after changing the setting.</string>
<string name="learn_more">Learn more about disappearing messages</string>
<string name="disappearing_messages_summary">Make future messages in this conversation automatically disappear 7\u00A0days after being received.</string>
<!-- Settings Feedback --> <!-- Settings Feedback -->
<string name="feedback_settings_title">Feedback</string> <string name="feedback_settings_title">Feedback</string>
<string name="send_feedback">Send feedback</string> <string name="send_feedback">Send feedback</string>

View File

@@ -1,23 +0,0 @@
package org.briarproject.briar.api.autodelete;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MINUTES;
public interface AutoDeleteConstants {
/**
* The minimum valid auto-delete timer duration in milliseconds.
*/
long MIN_AUTO_DELETE_TIMER_MS = MINUTES.toMillis(1);
/**
* The maximum valid auto-delete timer duration in milliseconds.
*/
long MAX_AUTO_DELETE_TIMER_MS = DAYS.toMillis(365);
/**
* Placeholder value indicating that a message has no auto-delete timer.
* This value should not be sent over the wire - send null instead.
*/
long NO_AUTO_DELETE_TIMER = -1;
}

View File

@@ -1,56 +0,0 @@
package org.briarproject.briar.api.autodelete;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
@NotNullByDefault
public interface AutoDeleteManager {
/**
* The unique ID of the auto-delete client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.autodelete");
/**
* The current major version of the auto-delete client.
*/
int MAJOR_VERSION = 0;
/**
* The current minor version of the auto-delete client.
*/
int MINOR_VERSION = 0;
/**
* Returns the auto-delete timer duration for the given contact. Use
* {@link #getAutoDeleteTimer(Transaction, ContactId, long)} if the timer
* will be used in an outgoing message.
*/
long getAutoDeleteTimer(Transaction txn, ContactId c) throws DbException;
/**
* Returns the auto-delete timer duration for the given contact, for use in
* a message with the given timestamp. The timestamp is stored.
*/
long getAutoDeleteTimer(Transaction txn, ContactId c, long timestamp)
throws DbException;
/**
* Sets the auto-delete timer duration for the given contact.
*/
void setAutoDeleteTimer(Transaction txn, ContactId c, long timer)
throws DbException;
/**
* Receives an auto-delete timer duration from the given contact, carried
* in a message with the given timestamp. The local timer is set to the
* same duration unless it has been
* {@link #setAutoDeleteTimer(Transaction, ContactId, long) changed} more
* recently than the remote timer.
*/
void receiveAutoDeleteTimer(Transaction txn, ContactId c, long timer,
long timestamp) throws DbException;
}

View File

@@ -15,9 +15,9 @@ public class BlogInvitationRequest extends InvitationRequest<Blog> {
public BlogInvitationRequest(MessageId id, GroupId groupId, long time, public BlogInvitationRequest(MessageId id, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, Blog blog, @Nullable String text, SessionId sessionId, Blog blog, @Nullable String text,
boolean available, boolean canBeOpened, long autoDeleteTimer) { boolean available, boolean canBeOpened) {
super(id, groupId, time, local, read, sent, seen, sessionId, blog, super(id, groupId, time, local, read, sent, seen, sessionId, blog,
text, available, canBeOpened, autoDeleteTimer); text, available, canBeOpened);
} }
@Override @Override

View File

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

View File

@@ -18,5 +18,5 @@ public interface BlogSharingManager extends SharingManager<Blog> {
/** /**
* The current minor version of the blog sharing client. * The current minor version of the blog sharing client.
*/ */
int MINOR_VERSION = 1; int MINOR_VERSION = 0;
} }

View File

@@ -43,18 +43,6 @@ 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

@@ -12,20 +12,18 @@ public abstract class ConversationMessageHeader {
private final MessageId id; private final MessageId id;
private final GroupId groupId; private final GroupId groupId;
private final long timestamp, autoDeleteTimer; private final long timestamp;
private final boolean local, read, sent, seen; private final boolean local, sent, seen, read;
public ConversationMessageHeader(MessageId id, GroupId groupId, public ConversationMessageHeader(MessageId id, GroupId groupId, long timestamp,
long timestamp, boolean local, boolean read, boolean sent, boolean local, boolean read, boolean sent, boolean seen) {
boolean seen, long autoDeleteTimer) {
this.id = id; this.id = id;
this.groupId = groupId; this.groupId = groupId;
this.timestamp = timestamp; this.timestamp = timestamp;
this.local = local; this.local = local;
this.read = read;
this.sent = sent; this.sent = sent;
this.seen = seen; this.seen = seen;
this.autoDeleteTimer = autoDeleteTimer; this.read = read;
} }
public MessageId getId() { public MessageId getId() {
@@ -57,8 +55,4 @@ public abstract class ConversationMessageHeader {
} }
public abstract <T> T accept(ConversationMessageVisitor<T> v); public abstract <T> T accept(ConversationMessageVisitor<T> v);
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
} }

View File

@@ -20,12 +20,11 @@ public abstract class ConversationRequest<N extends Nameable>
private final String text; private final String text;
private final boolean answered; private final boolean answered;
public ConversationRequest(MessageId messageId, GroupId groupId, public ConversationRequest(MessageId messageId, GroupId groupId, long time,
long timestamp, boolean local, boolean read, boolean sent, boolean local, boolean read, boolean sent, boolean seen,
boolean seen, SessionId sessionId, N nameable, SessionId sessionId, N nameable, @Nullable String text,
@Nullable String text, boolean answered, long autoDeleteTimer) { boolean answered) {
super(messageId, groupId, timestamp, local, read, sent, seen, super(messageId, groupId, time, local, read, sent, seen);
autoDeleteTimer);
this.sessionId = sessionId; this.sessionId = sessionId;
this.nameable = nameable; this.nameable = nameable;
this.text = text; this.text = text;

View File

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

View File

@@ -17,9 +17,9 @@ public class ForumInvitationRequest extends InvitationRequest<Forum> {
public ForumInvitationRequest(MessageId id, GroupId groupId, long time, public ForumInvitationRequest(MessageId id, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, Forum forum, @Nullable String text, SessionId sessionId, Forum forum, @Nullable String text,
boolean available, boolean canBeOpened, long autoDeleteTimer) { boolean available, boolean canBeOpened) {
super(id, groupId, time, local, read, sent, seen, sessionId, forum, super(id, groupId, time, local, read, sent, seen, sessionId, forum,
text, available, canBeOpened, autoDeleteTimer); text, available, canBeOpened);
} }
@Override @Override

View File

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

View File

@@ -18,5 +18,5 @@ public interface ForumSharingManager extends SharingManager<Forum> {
/** /**
* The current minor version of the forum sharing client. * The current minor version of the forum sharing client.
*/ */
int MINOR_VERSION = 1; int MINOR_VERSION = 0;
} }

View File

@@ -31,18 +31,18 @@ public interface IntroductionManager extends ConversationClient {
/** /**
* The current minor version of the introduction client. * The current minor version of the introduction client.
*/ */
int MINOR_VERSION = 1; int MINOR_VERSION = 0;
/** /**
* 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,
throws DbException; long timestamp) throws DbException;
/** /**
* Responds to an introduction. * Responds to an introduction.
*/ */
void respondToIntroduction(ContactId contactId, SessionId sessionId, void respondToIntroduction(ContactId contactId, SessionId sessionId,
boolean accept) throws DbException; long timestamp, boolean accept) throws DbException;
} }

View File

@@ -18,12 +18,12 @@ public class IntroductionRequest extends ConversationRequest<Author> {
private final AuthorInfo authorInfo; private final AuthorInfo authorInfo;
public IntroductionRequest(MessageId messageId, GroupId groupId, long time, public IntroductionRequest(MessageId messageId, GroupId groupId,
boolean local, boolean read, boolean sent, boolean seen, long time, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, Author author, @Nullable String text, SessionId sessionId, Author author, @Nullable String text,
boolean answered, AuthorInfo authorInfo, long autoDeleteTimer) { boolean answered, AuthorInfo authorInfo) {
super(messageId, groupId, time, local, read, sent, seen, sessionId, super(messageId, groupId, time, local, read, sent, seen, sessionId,
author, text, answered, autoDeleteTimer); author, text, answered);
this.authorInfo = authorInfo; this.authorInfo = authorInfo;
} }

View File

@@ -25,10 +25,9 @@ public class IntroductionResponse extends ConversationResponse {
public IntroductionResponse(MessageId messageId, GroupId groupId, long time, public IntroductionResponse(MessageId messageId, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, boolean accepted, Author author, SessionId sessionId, boolean accepted, Author author,
AuthorInfo introducedAuthorInfo, Role role, boolean canSucceed, AuthorInfo introducedAuthorInfo, Role role, boolean canSucceed) {
long autoDeleteTimer) {
super(messageId, groupId, time, local, read, sent, seen, sessionId, super(messageId, groupId, time, local, read, sent, seen, sessionId,
accepted, autoDeleteTimer); accepted);
this.introducedAuthor = author; this.introducedAuthor = author;
this.introducedAuthorInfo = introducedAuthorInfo; this.introducedAuthorInfo = introducedAuthorInfo;
this.ourRole = role; this.ourRole = role;

View File

@@ -30,18 +30,13 @@ public interface MessagingManager extends ConversationClient {
/** /**
* The current minor version of the messaging client. * The current minor version of the messaging client.
*/ */
int MINOR_VERSION = 3; int MINOR_VERSION = 2;
/** /**
* Stores a local private message. * Stores a local private message.
*/ */
void addLocalMessage(PrivateMessage m) throws DbException; void addLocalMessage(PrivateMessage m) throws DbException;
/**
* Stores a local private message.
*/
void addLocalMessage(Transaction txn, PrivateMessage m) throws DbException;
/** /**
* Stores a local attachment message. * Stores a local attachment message.
* *
@@ -82,8 +77,12 @@ public interface MessagingManager extends ConversationClient {
Attachment getAttachment(AttachmentHeader h) throws DbException; Attachment getAttachment(AttachmentHeader h) throws DbException;
/** /**
* Returns the private message format supported by the given contact. * Returns true if the contact with the given {@link ContactId} does support
* image attachments.
*
* Added: 2019-01-01
*/ */
PrivateMessageFormat getContactMessageFormat(Transaction txn, ContactId c) boolean contactSupportsImages(Transaction txn, ContactId c)
throws DbException; throws DbException;
} }

View File

@@ -8,66 +8,44 @@ import java.util.List;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES_AUTO_DELETE;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_ONLY;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class PrivateMessage { public class PrivateMessage {
private final Message message; private final Message message;
private final boolean hasText; private final boolean legacyFormat, hasText;
private final List<AttachmentHeader> attachmentHeaders; private final List<AttachmentHeader> attachmentHeaders;
private final long autoDeleteTimer;
private final PrivateMessageFormat format;
/** /**
* Constructor for private messages in the * Constructor for private messages in the legacy format, which does not
* {@link PrivateMessageFormat#TEXT_ONLY TEXT_ONLY} format. * support attachments.
*/ */
public PrivateMessage(Message message) { public PrivateMessage(Message message) {
this.message = message; this.message = message;
legacyFormat = true;
hasText = true; hasText = true;
attachmentHeaders = emptyList(); attachmentHeaders = emptyList();
autoDeleteTimer = NO_AUTO_DELETE_TIMER;
format = TEXT_ONLY;
} }
/** /**
* Constructor for private messages in the * Constructor for private messages in the current format, which supports
* {@link PrivateMessageFormat#TEXT_IMAGES TEXT_IMAGES} format. * attachments.
*/ */
public PrivateMessage(Message message, boolean hasText, public PrivateMessage(Message message, boolean hasText,
List<AttachmentHeader> headers) { List<AttachmentHeader> headers) {
this.message = message; this.message = message;
this.hasText = hasText; this.hasText = hasText;
this.attachmentHeaders = headers; this.attachmentHeaders = headers;
autoDeleteTimer = NO_AUTO_DELETE_TIMER; legacyFormat = false;
format = TEXT_IMAGES;
}
/**
* Constructor for private messages in the
* {@link PrivateMessageFormat#TEXT_IMAGES_AUTO_DELETE TEXT_IMAGES_AUTO_DELETE}
* format.
*/
public PrivateMessage(Message message, boolean hasText,
List<AttachmentHeader> headers, long autoDeleteTimer) {
this.message = message;
this.hasText = hasText;
this.attachmentHeaders = headers;
this.autoDeleteTimer = autoDeleteTimer;
format = TEXT_IMAGES_AUTO_DELETE;
} }
public Message getMessage() { public Message getMessage() {
return message; return message;
} }
public PrivateMessageFormat getFormat() { public boolean isLegacyFormat() {
return format; return legacyFormat;
} }
public boolean hasText() { public boolean hasText() {
@@ -77,8 +55,4 @@ public class PrivateMessage {
public List<AttachmentHeader> getAttachmentHeaders() { public List<AttachmentHeader> getAttachmentHeaders() {
return attachmentHeaders; return attachmentHeaders;
} }
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
} }

View File

@@ -11,29 +11,11 @@ import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public interface PrivateMessageFactory { public interface PrivateMessageFactory {
/**
* Creates a private message in the
* {@link PrivateMessageFormat#TEXT_ONLY TEXT_ONLY} format.
*/
PrivateMessage createLegacyPrivateMessage(GroupId groupId, long timestamp, PrivateMessage createLegacyPrivateMessage(GroupId groupId, long timestamp,
String text) throws FormatException; String text) throws FormatException;
/**
* Creates a private message in the
* {@link PrivateMessageFormat#TEXT_IMAGES TEXT_IMAGES} format. This format
* requires the contact to support client version 0.1 or higher.
*/
PrivateMessage createPrivateMessage(GroupId groupId, long timestamp, PrivateMessage createPrivateMessage(GroupId groupId, long timestamp,
@Nullable String text, List<AttachmentHeader> headers) @Nullable String text, List<AttachmentHeader> headers)
throws FormatException; throws FormatException;
/**
* Creates a private message in the
* {@link PrivateMessageFormat#TEXT_IMAGES_AUTO_DELETE TEXT_IMAGES_AUTO_DELETE}
* format. This format requires the contact to support client version 0.3
* or higher.
*/
PrivateMessage createPrivateMessage(GroupId groupId, long timestamp,
@Nullable String text, List<AttachmentHeader> headers,
long autoDeleteTimer) throws FormatException;
} }

View File

@@ -1,24 +0,0 @@
package org.briarproject.briar.api.messaging;
public enum PrivateMessageFormat {
/**
* First version of the private message format, which doesn't support
* image attachments or auto-deletion.
*/
TEXT_ONLY,
/**
* Second version of the private message format, which supports image
* attachments but not auto-deletion. Support for this format was
* added in client version 0.1.
*/
TEXT_IMAGES,
/**
* Third version of the private message format, which supports image
* attachments and auto-deletion. Support for this format was added
* in client version 0.3.
*/
TEXT_IMAGES_AUTO_DELETE
}

View File

@@ -19,9 +19,8 @@ public class PrivateMessageHeader extends ConversationMessageHeader {
public PrivateMessageHeader(MessageId id, GroupId groupId, long timestamp, public PrivateMessageHeader(MessageId id, GroupId groupId, long timestamp,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
boolean hasText, List<AttachmentHeader> headers, boolean hasText, List<AttachmentHeader> headers) {
long autoDeleteTimer) { super(id, groupId, timestamp, local, read, sent, seen);
super(id, groupId, timestamp, local, read, sent, seen, autoDeleteTimer);
this.hasText = hasText; this.hasText = hasText;
this.attachmentHeaders = headers; this.attachmentHeaders = headers;
} }
@@ -38,4 +37,5 @@ public class PrivateMessageHeader extends ConversationMessageHeader {
public <T> T accept(ConversationMessageVisitor<T> v) { public <T> T accept(ConversationMessageVisitor<T> v) {
return v.visitPrivateMessageHeader(this); return v.visitPrivateMessageHeader(this);
} }
} }

View File

@@ -32,7 +32,7 @@ public interface GroupInvitationManager extends ConversationClient {
/** /**
* The current minor version of the private group invitation client. * The current minor version of the private group invitation client.
*/ */
int MINOR_VERSION = 1; int MINOR_VERSION = 0;
/** /**
* Sends an invitation to share the given private group with the given * Sends an invitation to share the given private group with the given
@@ -43,8 +43,7 @@ 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, long autoDeleteTimer) long timestamp, byte[] signature) throws DbException;
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

@@ -18,10 +18,9 @@ public class GroupInvitationRequest extends InvitationRequest<PrivateGroup> {
public GroupInvitationRequest(MessageId id, GroupId groupId, long time, public GroupInvitationRequest(MessageId id, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, PrivateGroup shareable, SessionId sessionId, PrivateGroup shareable,
@Nullable String text, boolean available, boolean canBeOpened, @Nullable String text, boolean available, boolean canBeOpened) {
long autoDeleteTimer) {
super(id, groupId, time, local, read, sent, seen, sessionId, shareable, super(id, groupId, time, local, read, sent, seen, sessionId, shareable,
text, available, canBeOpened, autoDeleteTimer); text, available, canBeOpened);
} }
@Override @Override

View File

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

View File

@@ -15,9 +15,9 @@ public abstract class InvitationRequest<S extends Shareable> extends
public InvitationRequest(MessageId messageId, GroupId groupId, long time, public InvitationRequest(MessageId messageId, GroupId groupId, long time,
boolean local, boolean read, boolean sent, boolean seen, boolean local, boolean read, boolean sent, boolean seen,
SessionId sessionId, S object, @Nullable String text, SessionId sessionId, S object, @Nullable String text,
boolean available, boolean canBeOpened, long autoDeleteTimer) { boolean available, boolean canBeOpened) {
super(messageId, groupId, time, local, read, sent, seen, sessionId, super(messageId, groupId, time, local, read, sent, seen, sessionId,
object, text, !available, autoDeleteTimer); object, text, !available);
this.canBeOpened = canBeOpened; this.canBeOpened = canBeOpened;
} }

View File

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

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) throws DbException; @Nullable String text, long timestamp) throws DbException;
/** /**
* Responds to a pending group invitation * Responds to a pending group invitation

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar; package org.briarproject.briar;
import org.briarproject.briar.autodelete.AutoDeleteModule;
import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.feed.FeedModule; import org.briarproject.briar.feed.FeedModule;
import org.briarproject.briar.forum.ForumModule; import org.briarproject.briar.forum.ForumModule;
@@ -12,8 +11,6 @@ import org.briarproject.briar.sharing.SharingModule;
public interface BriarCoreEagerSingletons { public interface BriarCoreEagerSingletons {
void inject(AutoDeleteModule.EagerSingletons init);
void inject(BlogModule.EagerSingletons init); void inject(BlogModule.EagerSingletons init);
void inject(FeedModule.EagerSingletons init); void inject(FeedModule.EagerSingletons init);
@@ -33,7 +30,6 @@ public interface BriarCoreEagerSingletons {
class Helper { class Helper {
public static void injectEagerSingletons(BriarCoreEagerSingletons c) { public static void injectEagerSingletons(BriarCoreEagerSingletons c) {
c.inject(new AutoDeleteModule.EagerSingletons());
c.inject(new BlogModule.EagerSingletons()); c.inject(new BlogModule.EagerSingletons());
c.inject(new FeedModule.EagerSingletons()); c.inject(new FeedModule.EagerSingletons());
c.inject(new ForumModule.EagerSingletons()); c.inject(new ForumModule.EagerSingletons());

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar; package org.briarproject.briar;
import org.briarproject.briar.autodelete.AutoDeleteModule;
import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule; import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.feed.DnsModule; import org.briarproject.briar.feed.DnsModule;
@@ -16,7 +15,6 @@ import org.briarproject.briar.test.TestModule;
import dagger.Module; import dagger.Module;
@Module(includes = { @Module(includes = {
AutoDeleteModule.class,
BlogModule.class, BlogModule.class,
BriarClientModule.class, BriarClientModule.class,
FeedModule.class, FeedModule.class,

View File

@@ -1,29 +0,0 @@
package org.briarproject.briar.autodelete;
interface AutoDeleteConstants {
/**
* Group metadata key for storing the auto-delete timer duration.
*/
String GROUP_KEY_TIMER = "autoDeleteTimer";
/**
* Group metadata key for storing the timestamp of the latest incoming or
* outgoing message carrying an auto-delete timer (including a null timer).
*/
String GROUP_KEY_TIMESTAMP = "autoDeleteTimestamp";
/**
* Group metadata key for storing the previous auto-delete timer duration.
* This is used to decide whether a local change to the duration should be
* overwritten by a duration received from the contact.
*/
String GROUP_KEY_PREVIOUS_TIMER = "autoDeletePreviousTimer";
/**
* Special value for {@link #GROUP_KEY_PREVIOUS_TIMER} indicating that
* there are no local changes to the auto-delete timer duration that need
* to be compared with durations received from the contact.
*/
long NO_PREVIOUS_TIMER = 0;
}

View File

@@ -1,188 +0,0 @@
package org.briarproject.briar.autodelete;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_PREVIOUS_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMESTAMP;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.NO_PREVIOUS_TIMER;
@Immutable
@NotNullByDefault
class AutoDeleteManagerImpl
implements AutoDeleteManager, OpenDatabaseHook, ContactHook {
private static final Logger LOG =
getLogger(AutoDeleteManagerImpl.class.getName());
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final GroupFactory groupFactory;
private final Group localGroup;
@Inject
AutoDeleteManagerImpl(
DatabaseComponent db,
ClientHelper clientHelper,
GroupFactory groupFactory,
ContactGroupFactory contactGroupFactory) {
this.db = db;
this.clientHelper = clientHelper;
this.groupFactory = groupFactory;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
MAJOR_VERSION);
}
@Override
public void onDatabaseOpened(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup);
// Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
}
@Override
public void addingContact(Transaction txn, Contact c) throws DbException {
Group g = getGroup(c);
db.addGroup(txn, g);
clientHelper.setContactId(txn, g.getId(), c.getId());
}
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getGroup(c));
}
@Override
public long getAutoDeleteTimer(Transaction txn, ContactId c)
throws DbException {
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
return meta.getLong(GROUP_KEY_TIMER, NO_AUTO_DELETE_TIMER);
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public long getAutoDeleteTimer(Transaction txn, ContactId c, long timestamp)
throws DbException {
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
long timer = meta.getLong(GROUP_KEY_TIMER, NO_AUTO_DELETE_TIMER);
if (LOG.isLoggable(INFO)) {
LOG.info("Sending message with auto-delete timer " + timer);
}
// Update the timestamp and clear the previous timer, if any
meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, timestamp),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
return timer;
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public void setAutoDeleteTimer(Transaction txn, ContactId c, long timer)
throws DbException {
validateTimer(timer);
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
long oldTimer = meta.getLong(GROUP_KEY_TIMER, NO_AUTO_DELETE_TIMER);
if (timer == oldTimer) return;
if (LOG.isLoggable(INFO)) {
LOG.info("Setting auto-delete timer to " + timer);
}
// Store the new timer and the previous timer
meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, timer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, oldTimer));
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public void receiveAutoDeleteTimer(Transaction txn, ContactId c,
long timer, long timestamp) throws DbException {
validateTimer(timer);
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
long oldTimestamp = meta.getLong(GROUP_KEY_TIMESTAMP, 0L);
if (timestamp <= oldTimestamp) return;
long oldTimer =
meta.getLong(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER);
meta = new BdfDictionary();
if (oldTimer == NO_PREVIOUS_TIMER) {
// We don't have an unsent change. Mirror their timer
if (LOG.isLoggable(INFO)) {
LOG.info("Mirroring auto-delete timer " + timer);
}
meta.put(GROUP_KEY_TIMER, timer);
} else if (timer != oldTimer) {
// Their sent change trumps our unsent change. Mirror their
// timer and clear the previous timer to drop our unsent change
if (LOG.isLoggable(INFO)) {
LOG.info("Mirroring auto-delete timer " + timer
+ " and forgetting unsent change");
}
meta.put(GROUP_KEY_TIMER, timer);
meta.put(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER);
}
// Always update the timestamp
meta.put(GROUP_KEY_TIMESTAMP, timestamp);
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new DbException(e);
}
}
private Group getGroup(Contact c) {
byte[] descriptor = c.getAuthor().getId().getBytes();
return groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION, descriptor);
}
private void validateTimer(long timer) {
if (timer != NO_AUTO_DELETE_TIMER &&
(timer < MIN_AUTO_DELETE_TIMER_MS ||
timer > MAX_AUTO_DELETE_TIMER_MS)) {
throw new IllegalArgumentException();
}
}
}

View File

@@ -1,32 +0,0 @@
package org.briarproject.briar.autodelete;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class AutoDeleteModule {
public static class EagerSingletons {
@Inject
AutoDeleteManager autoDeleteManager;
}
@Provides
@Singleton
AutoDeleteManager provideAutoDeleteManager(
LifecycleManager lifecycleManager, ContactManager contactManager,
AutoDeleteManagerImpl autoDeleteManager) {
lifecycleManager.registerOpenDatabaseHook(autoDeleteManager);
contactManager.registerContactHook(autoDeleteManager);
// Don't need to register with the client versioning manager as this
// client's groups aren't shared with contacts
return autoDeleteManager;
}
}

View File

@@ -8,8 +8,6 @@ import org.briarproject.briar.api.client.SessionId;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class AbortMessage extends AbstractIntroductionMessage { class AbortMessage extends AbstractIntroductionMessage {
@@ -18,8 +16,7 @@ class AbortMessage extends AbstractIntroductionMessage {
protected AbortMessage(MessageId messageId, GroupId groupId, long timestamp, protected AbortMessage(MessageId messageId, GroupId groupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId) { @Nullable MessageId previousMessageId, SessionId sessionId) {
super(messageId, groupId, timestamp, previousMessageId, super(messageId, groupId, timestamp, previousMessageId);
NO_AUTO_DELETE_TIMER);
this.sessionId = sessionId; this.sessionId = sessionId;
} }

View File

@@ -16,16 +16,13 @@ abstract class AbstractIntroductionMessage {
private final long timestamp; private final long timestamp;
@Nullable @Nullable
private final MessageId previousMessageId; private final MessageId previousMessageId;
private final long autoDeleteTimer;
AbstractIntroductionMessage(MessageId messageId, GroupId groupId, AbstractIntroductionMessage(MessageId messageId, GroupId groupId,
long timestamp, @Nullable MessageId previousMessageId, long timestamp, @Nullable MessageId previousMessageId) {
long autoDeleteTimer) {
this.messageId = messageId; this.messageId = messageId;
this.groupId = groupId; this.groupId = groupId;
this.timestamp = timestamp; this.timestamp = timestamp;
this.previousMessageId = previousMessageId; this.previousMessageId = previousMessageId;
this.autoDeleteTimer = autoDeleteTimer;
} }
MessageId getMessageId() { MessageId getMessageId() {
@@ -45,7 +42,4 @@ abstract class AbstractIntroductionMessage {
return previousMessageId; return previousMessageId;
} }
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
@@ -18,15 +17,11 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.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;
@@ -35,9 +30,6 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION;
import static org.briarproject.briar.introduction.MessageType.ABORT; import static org.briarproject.briar.introduction.MessageType.ABORT;
import static org.briarproject.briar.introduction.MessageType.ACCEPT; import static org.briarproject.briar.introduction.MessageType.ACCEPT;
import static org.briarproject.briar.introduction.MessageType.ACTIVATE; import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
@@ -47,7 +39,7 @@ import static org.briarproject.briar.introduction.MessageType.REQUEST;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
abstract class AbstractProtocolEngine<S extends Session<?>> abstract class AbstractProtocolEngine<S extends Session>
implements ProtocolEngine<S> { implements ProtocolEngine<S> {
protected final DatabaseComponent db; protected final DatabaseComponent db;
@@ -58,9 +50,6 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
protected final IdentityManager identityManager; protected final IdentityManager identityManager;
protected final MessageParser messageParser; protected final MessageParser messageParser;
protected final MessageEncoder messageEncoder; protected final MessageEncoder messageEncoder;
protected final ClientVersioningManager clientVersioningManager;
protected final AutoDeleteManager autoDeleteManager;
protected final ConversationManager conversationManager;
protected final Clock clock; protected final Clock clock;
AbstractProtocolEngine( AbstractProtocolEngine(
@@ -72,9 +61,6 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
IdentityManager identityManager, IdentityManager identityManager,
MessageParser messageParser, MessageParser messageParser,
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
ClientVersioningManager clientVersioningManager,
AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
@@ -84,29 +70,16 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
this.identityManager = identityManager; this.identityManager = identityManager;
this.messageParser = messageParser; this.messageParser = messageParser;
this.messageEncoder = messageEncoder; this.messageEncoder = messageEncoder;
this.clientVersioningManager = clientVersioningManager;
this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
this.clock = clock; this.clock = clock;
} }
Message sendRequestMessage(Transaction txn, PeerSession s, Message sendRequestMessage(Transaction txn, PeerSession s,
long timestamp, Author author, @Nullable String text) long timestamp, Author author, @Nullable String text)
throws DbException { throws DbException {
Message m; Message m = messageEncoder
ContactId c = getContactId(txn, s.getContactGroupId()); .encodeRequestMessage(s.getContactGroupId(), timestamp,
if (contactSupportsAutoDeletion(txn, c)) { s.getLastLocalMessageId(), author, text);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c, sendMessage(txn, REQUEST, s.getSessionId(), m, true);
timestamp);
m = messageEncoder.encodeRequestMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), author, text, timer);
sendMessage(txn, REQUEST, s.getSessionId(), m, true, timer);
} else {
m = messageEncoder.encodeRequestMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), author, text);
sendMessage(txn, REQUEST, s.getSessionId(), m, true,
NO_AUTO_DELETE_TIMER);
}
return m; return m;
} }
@@ -114,43 +87,21 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
PublicKey ephemeralPublicKey, long acceptTimestamp, PublicKey ephemeralPublicKey, long acceptTimestamp,
Map<TransportId, TransportProperties> transportProperties, Map<TransportId, TransportProperties> transportProperties,
boolean visible) throws DbException { boolean visible) throws DbException {
Message m; Message m = messageEncoder
ContactId c = getContactId(txn, s.getContactGroupId()); .encodeAcceptMessage(s.getContactGroupId(), timestamp,
if (contactSupportsAutoDeletion(txn, c)) { s.getLastLocalMessageId(), s.getSessionId(),
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c, ephemeralPublicKey, acceptTimestamp,
timestamp); transportProperties);
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(), sendMessage(txn, ACCEPT, s.getSessionId(), m, visible);
timestamp, s.getLastLocalMessageId(), s.getSessionId(),
ephemeralPublicKey, acceptTimestamp, transportProperties,
timer);
sendMessage(txn, ACCEPT, s.getSessionId(), m, visible, timer);
} else {
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), s.getSessionId(),
ephemeralPublicKey, acceptTimestamp, transportProperties);
sendMessage(txn, ACCEPT, s.getSessionId(), m, visible,
NO_AUTO_DELETE_TIMER);
}
return m; return m;
} }
Message sendDeclineMessage(Transaction txn, PeerSession s, long timestamp, Message sendDeclineMessage(Transaction txn, PeerSession s, long timestamp,
boolean visible) throws DbException { boolean visible) throws DbException {
Message m; Message m = messageEncoder
ContactId c = getContactId(txn, s.getContactGroupId()); .encodeDeclineMessage(s.getContactGroupId(), timestamp,
if (contactSupportsAutoDeletion(txn, c)) { s.getLastLocalMessageId(), s.getSessionId());
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c, sendMessage(txn, DECLINE, s.getSessionId(), m, visible);
timestamp);
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), s.getSessionId(),
timer);
sendMessage(txn, DECLINE, s.getSessionId(), m, visible, timer);
} else {
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), s.getSessionId());
sendMessage(txn, DECLINE, s.getSessionId(), m, visible,
NO_AUTO_DELETE_TIMER);
}
return m; return m;
} }
@@ -160,8 +111,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
.encodeAuthMessage(s.getContactGroupId(), timestamp, .encodeAuthMessage(s.getContactGroupId(), timestamp,
s.getLastLocalMessageId(), s.getSessionId(), mac, s.getLastLocalMessageId(), s.getSessionId(), mac,
signature); signature);
sendMessage(txn, AUTH, s.getSessionId(), m, false, sendMessage(txn, AUTH, s.getSessionId(), m, false);
NO_AUTO_DELETE_TIMER);
return m; return m;
} }
@@ -170,8 +120,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m = messageEncoder Message m = messageEncoder
.encodeActivateMessage(s.getContactGroupId(), timestamp, .encodeActivateMessage(s.getContactGroupId(), timestamp,
s.getLastLocalMessageId(), s.getSessionId(), mac); s.getLastLocalMessageId(), s.getSessionId(), mac);
sendMessage(txn, ACTIVATE, s.getSessionId(), m, false, sendMessage(txn, ACTIVATE, s.getSessionId(), m, false);
NO_AUTO_DELETE_TIMER);
return m; return m;
} }
@@ -180,17 +129,16 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m = messageEncoder Message m = messageEncoder
.encodeAbortMessage(s.getContactGroupId(), timestamp, .encodeAbortMessage(s.getContactGroupId(), timestamp,
s.getLastLocalMessageId(), s.getSessionId()); s.getLastLocalMessageId(), s.getSessionId());
sendMessage(txn, ABORT, s.getSessionId(), m, false, sendMessage(txn, ABORT, s.getSessionId(), m, false);
NO_AUTO_DELETE_TIMER);
return m; return m;
} }
private void sendMessage(Transaction txn, MessageType type, private void sendMessage(Transaction txn, MessageType type,
SessionId sessionId, Message m, boolean visibleInConversation, SessionId sessionId, Message m, boolean visibleInConversation)
long autoDeleteTimer) throws DbException { throws DbException {
BdfDictionary meta = messageEncoder.encodeMetadata(type, sessionId, BdfDictionary meta = messageEncoder
m.getTimestamp(), true, true, visibleInConversation, .encodeMetadata(type, sessionId, m.getTimestamp(), true, true,
autoDeleteTimer); visibleInConversation);
try { try {
clientHelper.addLocalMessage(txn, m, meta, true, false); clientHelper.addLocalMessage(txn, m, meta, true, false);
} catch (FormatException e) { } catch (FormatException e) {
@@ -198,10 +146,9 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
} }
} }
void broadcastIntroductionResponseReceivedEvent(Transaction txn, void broadcastIntroductionResponseReceivedEvent(Transaction txn, Session s,
Session<?> s, AuthorId sender, Author otherAuthor, AuthorId sender, Author otherAuthor, AbstractIntroductionMessage m,
AbstractIntroductionMessage m, boolean canSucceed) boolean canSucceed) throws DbException {
throws DbException {
AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId(); AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
Contact c = contactManager.getContact(txn, sender, localAuthorId); Contact c = contactManager.getContact(txn, sender, localAuthorId);
AuthorInfo otherAuthorInfo = AuthorInfo otherAuthorInfo =
@@ -210,8 +157,7 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
new IntroductionResponse(m.getMessageId(), m.getGroupId(), new IntroductionResponse(m.getMessageId(), m.getGroupId(),
m.getTimestamp(), false, false, false, false, m.getTimestamp(), false, false, false, false,
s.getSessionId(), m instanceof AcceptMessage, s.getSessionId(), m instanceof AcceptMessage,
otherAuthor, otherAuthorInfo, s.getRole(), canSucceed, otherAuthor, otherAuthorInfo, s.getRole(), canSucceed);
m.getAutoDeleteTimer());
IntroductionResponseReceivedEvent e = IntroductionResponseReceivedEvent e =
new IntroductionResponseReceivedEvent(response, c.getId()); new IntroductionResponseReceivedEvent(response, c.getId());
txn.attach(e); txn.attach(e);
@@ -234,33 +180,14 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
return !dependency.equals(lastRemoteMessageId); return !dependency.equals(lastRemoteMessageId);
} }
long getTimestampForOutgoingMessage(Transaction txn, GroupId contactGroupId) long getLocalTimestamp(long localTimestamp, long requestTimestamp) {
throws DbException { return Math.max(
ContactId c = getContactId(txn, contactGroupId); clock.currentTimeMillis(),
return conversationManager.getTimestampForOutgoingMessage(txn, c); Math.max(
localTimestamp,
requestTimestamp
) + 1
);
} }
void receiveAutoDeleteTimer(Transaction txn, AbstractIntroductionMessage m)
throws DbException {
ContactId c = getContactId(txn, m.getGroupId());
autoDeleteManager.receiveAutoDeleteTimer(txn, c, m.getAutoDeleteTimer(),
m.getTimestamp());
}
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException {
try {
return clientHelper.getContactId(txn, contactGroupId);
} catch (FormatException e) {
throw new DbException(e);
}
}
private boolean contactSupportsAutoDeletion(Transaction txn, ContactId c)
throws DbException {
int minorVersion = clientVersioningManager.getClientMinorVersion(txn, c,
CLIENT_ID, MAJOR_VERSION);
// Auto-delete was added in client version 0.1
return minorVersion >= 1;
}
} }

View File

@@ -26,10 +26,8 @@ class AcceptMessage extends AbstractIntroductionMessage {
long timestamp, @Nullable MessageId previousMessageId, long timestamp, @Nullable MessageId previousMessageId,
SessionId sessionId, PublicKey ephemeralPublicKey, SessionId sessionId, PublicKey ephemeralPublicKey,
long acceptTimestamp, long acceptTimestamp,
Map<TransportId, TransportProperties> transportProperties, Map<TransportId, TransportProperties> transportProperties) {
long autoDeleteTimer) { super(messageId, groupId, timestamp, previousMessageId);
super(messageId, groupId, timestamp, previousMessageId,
autoDeleteTimer);
this.sessionId = sessionId; this.sessionId = sessionId;
this.ephemeralPublicKey = ephemeralPublicKey; this.ephemeralPublicKey = ephemeralPublicKey;
this.acceptTimestamp = acceptTimestamp; this.acceptTimestamp = acceptTimestamp;

View File

@@ -7,8 +7,6 @@ import org.briarproject.briar.api.client.SessionId;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class ActivateMessage extends AbstractIntroductionMessage { class ActivateMessage extends AbstractIntroductionMessage {
@@ -19,8 +17,7 @@ class ActivateMessage extends AbstractIntroductionMessage {
protected ActivateMessage(MessageId messageId, GroupId groupId, protected ActivateMessage(MessageId messageId, GroupId groupId,
long timestamp, MessageId previousMessageId, SessionId sessionId, long timestamp, MessageId previousMessageId, SessionId sessionId,
byte[] mac) { byte[] mac) {
super(messageId, groupId, timestamp, previousMessageId, super(messageId, groupId, timestamp, previousMessageId);
NO_AUTO_DELETE_TIMER);
this.sessionId = sessionId; this.sessionId = sessionId;
this.mac = mac; this.mac = mac;
} }

View File

@@ -7,8 +7,6 @@ import org.briarproject.briar.api.client.SessionId;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class AuthMessage extends AbstractIntroductionMessage { class AuthMessage extends AbstractIntroductionMessage {
@@ -19,8 +17,7 @@ class AuthMessage extends AbstractIntroductionMessage {
protected AuthMessage(MessageId messageId, GroupId groupId, protected AuthMessage(MessageId messageId, GroupId groupId,
long timestamp, MessageId previousMessageId, SessionId sessionId, long timestamp, MessageId previousMessageId, SessionId sessionId,
byte[] mac, byte[] signature) { byte[] mac, byte[] signature) {
super(messageId, groupId, timestamp, previousMessageId, super(messageId, groupId, timestamp, previousMessageId);
NO_AUTO_DELETE_TIMER);
this.sessionId = sessionId; this.sessionId = sessionId;
this.mac = mac; this.mac = mac;
this.signature = signature; this.signature = signature;

View File

@@ -16,9 +16,8 @@ class DeclineMessage extends AbstractIntroductionMessage {
protected DeclineMessage(MessageId messageId, GroupId groupId, protected DeclineMessage(MessageId messageId, GroupId groupId,
long timestamp, @Nullable MessageId previousMessageId, long timestamp, @Nullable MessageId previousMessageId,
SessionId sessionId, long autoDeleteTimer) { SessionId sessionId) {
super(messageId, groupId, timestamp, previousMessageId, super(messageId, groupId, timestamp, previousMessageId);
autoDeleteTimer);
this.sessionId = sessionId; this.sessionId = sessionId;
} }

View File

@@ -26,12 +26,9 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
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;
@@ -44,7 +41,6 @@ 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;
@@ -77,17 +73,13 @@ 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,
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);
conversationManager, clock);
this.crypto = crypto; this.crypto = crypto;
this.keyManager = keyManager; this.keyManager = keyManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
@@ -95,18 +87,18 @@ class IntroduceeProtocolEngine
@Override @Override
public IntroduceeSession onRequestAction(Transaction txn, public IntroduceeSession onRequestAction(Transaction txn,
IntroduceeSession session, @Nullable String text) { IntroduceeSession session, @Nullable String text, long timestamp) {
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) throws DbException { IntroduceeSession session, long timestamp) 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); return onLocalAccept(txn, session, timestamp);
case START: case START:
case LOCAL_DECLINED: case LOCAL_DECLINED:
case LOCAL_ACCEPTED: case LOCAL_ACCEPTED:
@@ -120,12 +112,12 @@ class IntroduceeProtocolEngine
@Override @Override
public IntroduceeSession onDeclineAction(Transaction txn, public IntroduceeSession onDeclineAction(Transaction txn,
IntroduceeSession session) throws DbException { IntroduceeSession session, long timestamp) 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); return onLocalDecline(txn, session, timestamp);
case START: case START:
case LOCAL_DECLINED: case LOCAL_DECLINED:
case LOCAL_ACCEPTED: case LOCAL_ACCEPTED:
@@ -257,9 +249,6 @@ class IntroduceeProtocolEngine
messageTracker messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Broadcast IntroductionRequestReceivedEvent // Broadcast IntroductionRequestReceivedEvent
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
Contact c = contactManager.getContact(txn, s.getIntroducer().getId(), Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
@@ -269,7 +258,7 @@ class IntroduceeProtocolEngine
IntroductionRequest request = new IntroductionRequest(m.getMessageId(), IntroductionRequest request = new IntroductionRequest(m.getMessageId(),
m.getGroupId(), m.getTimestamp(), false, false, false, false, m.getGroupId(), m.getTimestamp(), false, false, false, false,
s.getSessionId(), m.getAuthor(), m.getText(), false, s.getSessionId(), m.getAuthor(), m.getText(), false,
authorInfo, m.getAutoDeleteTimer()); authorInfo);
IntroductionRequestReceivedEvent e = IntroductionRequestReceivedEvent e =
new IntroductionRequestReceivedEvent(request, c.getId()); new IntroductionRequestReceivedEvent(request, c.getId());
txn.attach(e); txn.attach(e);
@@ -279,7 +268,7 @@ class IntroduceeProtocolEngine
} }
private IntroduceeSession onLocalAccept(Transaction txn, private IntroduceeSession onLocalAccept(Transaction txn,
IntroduceeSession s) throws DbException { IntroduceeSession s, long timestamp) throws DbException {
// Mark the request message unavailable to answer // Mark the request message unavailable to answer
markRequestsUnavailableToAnswer(txn, s); markRequestsUnavailableToAnswer(txn, s);
@@ -290,8 +279,8 @@ class IntroduceeProtocolEngine
Map<TransportId, TransportProperties> transportProperties = Map<TransportId, TransportProperties> transportProperties =
transportPropertyManager.getLocalProperties(txn); transportPropertyManager.getLocalProperties(txn);
// Send an ACCEPT message // Send a ACCEPT message
long localTimestamp = getTimestampForVisibleMessage(txn, s); long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(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
@@ -316,12 +305,12 @@ class IntroduceeProtocolEngine
} }
private IntroduceeSession onLocalDecline(Transaction txn, private IntroduceeSession onLocalDecline(Transaction txn,
IntroduceeSession s) throws DbException { IntroduceeSession s, long timestamp) 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 = getTimestampForVisibleMessage(txn, s); long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s));
Message sent = sendDeclineMessage(txn, s, localTimestamp, true); Message sent = sendDeclineMessage(txn, s, localTimestamp, true);
// Track the message // Track the message
@@ -335,7 +324,8 @@ class IntroduceeProtocolEngine
} }
private IntroduceeSession onRemoteAccept(Transaction txn, private IntroduceeSession onRemoteAccept(Transaction txn,
IntroduceeSession s, AcceptMessage m) throws DbException { IntroduceeSession s, AcceptMessage m)
throws DbException {
// The timestamp must be higher than the last request message // The timestamp must be higher than the last request message
if (m.getTimestamp() <= s.getRequestTimestamp()) if (m.getTimestamp() <= s.getRequestTimestamp())
return abort(txn, s); return abort(txn, s);
@@ -372,9 +362,6 @@ class IntroduceeProtocolEngine
messageTracker messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Broadcast IntroductionResponseReceivedEvent // Broadcast IntroductionResponseReceivedEvent
broadcastIntroductionResponseReceivedEvent(txn, s, broadcastIntroductionResponseReceivedEvent(txn, s,
s.getIntroducer().getId(), s.getRemote().author, m, false); s.getIntroducer().getId(), s.getRemote().author, m, false);
@@ -421,8 +408,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();
long localTimestamp = getTimestampForInvisibleMessage(s); Message sent = sendAuthMessage(txn, s, getLocalTimestamp(s), mac,
Message sent = sendAuthMessage(txn, s, localTimestamp, mac, signature); signature);
return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, sent, masterKey, return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, sent, masterKey,
aliceMacKey, bobMacKey); aliceMacKey, bobMacKey);
} }
@@ -473,8 +460,7 @@ class IntroduceeProtocolEngine
// send ACTIVATE message with a MAC // send ACTIVATE message with a MAC
byte[] mac = crypto.activateMac(s); byte[] mac = crypto.activateMac(s);
long localTimestamp = getTimestampForInvisibleMessage(s); Message sent = sendActivateMessage(txn, s, getLocalTimestamp(s), mac);
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);
@@ -525,8 +511,7 @@ class IntroduceeProtocolEngine
markRequestsUnavailableToAnswer(txn, s); markRequestsUnavailableToAnswer(txn, s);
// Send an ABORT message // Send an ABORT message
long localTimestamp = getTimestampForInvisibleMessage(s); Message sent = sendAbortMessage(txn, s, getLocalTimestamp(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()));
@@ -541,34 +526,9 @@ class IntroduceeProtocolEngine
return isInvalidDependency(s.getLastRemoteMessageId(), dependency); return isInvalidDependency(s.getLastRemoteMessageId(), dependency);
} }
/** private long getLocalTimestamp(IntroduceeSession s) {
* Returns a timestamp for a visible outgoing message. The timestamp is return getLocalTimestamp(s.getLocalTimestamp(),
* later than the timestamp of any message sent or received so far in the s.getRequestTimestamp());
* 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

@@ -13,11 +13,8 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.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;
@@ -25,7 +22,6 @@ 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,23 +50,19 @@ class IntroducerProtocolEngine
IdentityManager identityManager, IdentityManager identityManager,
MessageParser messageParser, MessageParser messageParser,
MessageEncoder messageEncoder, MessageEncoder messageEncoder,
ClientVersioningManager clientVersioningManager,
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);
conversationManager, clock);
} }
@Override @Override
public IntroducerSession onRequestAction(Transaction txn, public IntroducerSession onRequestAction(Transaction txn,
IntroducerSession s, @Nullable String text) IntroducerSession s, @Nullable String text, long timestamp)
throws DbException { throws DbException {
switch (s.getState()) { switch (s.getState()) {
case START: case START:
return onLocalRequest(txn, s, text); return onLocalRequest(txn, s, text, timestamp);
case AWAIT_RESPONSES: case AWAIT_RESPONSES:
case AWAIT_RESPONSE_A: case AWAIT_RESPONSE_A:
case AWAIT_RESPONSE_B: case AWAIT_RESPONSE_B:
@@ -90,24 +82,37 @@ class IntroducerProtocolEngine
@Override @Override
public IntroducerSession onAcceptAction(Transaction txn, public IntroducerSession onAcceptAction(Transaction txn,
IntroducerSession s) { IntroducerSession s, long timestamp) {
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) { IntroducerSession s, long timestamp) {
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 with remaining introducee // abort session
IntroducerSession s = abort(txn, session, remainingIntroducee); IntroducerSession s = abort(txn, session);
// 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(), s.getIntroduceeA(), s.getRequestTimestamp(), introduceeA, introduceeB);
s.getIntroduceeB());
} }
@Override @Override
@@ -217,13 +222,13 @@ class IntroducerProtocolEngine
} }
private IntroducerSession onLocalRequest(Transaction txn, private IntroducerSession onLocalRequest(Transaction txn,
IntroducerSession s, @Nullable String text) throws DbException { IntroducerSession s, @Nullable String text, long timestamp)
throws DbException {
// Send REQUEST messages // Send REQUEST messages
long timestampA = long maxIntroduceeTimestamp =
getTimestampForVisibleMessage(txn, s, s.getIntroduceeA()); Math.max(getLocalTimestamp(s, s.getIntroduceeA()),
long timestampB = getLocalTimestamp(s, s.getIntroduceeB()));
getTimestampForVisibleMessage(txn, s, s.getIntroduceeB()); long localTimestamp = Math.max(timestamp, maxIntroduceeTimestamp);
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(),
@@ -260,16 +265,14 @@ class IntroducerProtocolEngine
// Track the incoming message // Track the incoming message
messageTracker messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Forward ACCEPT message // Forward ACCEPT message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
// The forwarded message will not be visible to the introducee long timestamp = getLocalTimestamp(s, i);
long localTimestamp = getTimestampForInvisibleMessage(s, i); Message sent =
Message sent = sendAcceptMessage(txn, i, localTimestamp, sendAcceptMessage(txn, i, timestamp, m.getEphemeralPublicKey(),
m.getEphemeralPublicKey(), m.getAcceptTimestamp(), m.getAcceptTimestamp(), m.getTransportProperties(),
m.getTransportProperties(), false); false);
// Create the next state // Create the next state
IntroducerState state = AWAIT_AUTHS; IntroducerState state = AWAIT_AUTHS;
@@ -323,14 +326,10 @@ class IntroducerProtocolEngine
// Track the incoming message // Track the incoming message
messageTracker messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Forward ACCEPT message // Forward ACCEPT message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
// The forwarded message will not be visible to the introducee Message sent = sendAcceptMessage(txn, i, getLocalTimestamp(s, i),
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);
@@ -378,14 +377,11 @@ class IntroducerProtocolEngine
// Track the incoming message // Track the incoming message
messageTracker messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Forward DECLINE message // Forward DECLINE message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
// The forwarded message will be visible to the introducee long timestamp = getLocalTimestamp(s, i);
long localTimestamp = getTimestampForVisibleMessage(txn, s, i); Message sent = sendDeclineMessage(txn, i, timestamp, false);
Message sent = sendDeclineMessage(txn, i, localTimestamp, false);
// Create the next state // Create the next state
IntroducerState state = START; IntroducerState state = START;
@@ -433,14 +429,11 @@ class IntroducerProtocolEngine
// Track the incoming message // Track the incoming message
messageTracker messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Forward DECLINE message // Forward DECLINE message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
// The forwarded message will be visible to the introducee long timestamp = getLocalTimestamp(s, i);
long localTimestamp = getTimestampForVisibleMessage(txn, s, i); Message sent = sendDeclineMessage(txn, i, timestamp, false);
Message sent = sendDeclineMessage(txn, i, localTimestamp, false);
Introducee introduceeA, introduceeB; Introducee introduceeA, introduceeB;
Author sender, other; Author sender, other;
@@ -480,8 +473,8 @@ class IntroducerProtocolEngine
// Forward AUTH message // Forward AUTH message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long localTimestamp = getTimestampForInvisibleMessage(s, i); long timestamp = getLocalTimestamp(s, i);
Message sent = sendAuthMessage(txn, i, localTimestamp, m.getMac(), Message sent = sendAuthMessage(txn, i, timestamp, m.getMac(),
m.getSignature()); m.getSignature());
// Move to the next state // Move to the next state
@@ -516,8 +509,8 @@ class IntroducerProtocolEngine
// Forward ACTIVATE message // Forward ACTIVATE message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long localTimestamp = getTimestampForInvisibleMessage(s, i); long timestamp = getLocalTimestamp(s, i);
Message sent = sendActivateMessage(txn, i, localTimestamp, m.getMac()); Message sent = sendActivateMessage(txn, i, timestamp, m.getMac());
// Move to the next state // Move to the next state
IntroducerState state = START; IntroducerState state = START;
@@ -539,8 +532,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 localTimestamp = getTimestampForInvisibleMessage(s, i); long timestamp = getLocalTimestamp(s, i);
Message sent = sendAbortMessage(txn, i, localTimestamp); Message sent = sendAbortMessage(txn, i, timestamp);
// Broadcast abort event for testing // Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId())); txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -558,45 +551,15 @@ class IntroducerProtocolEngine
s.getRequestTimestamp(), introduceeA, introduceeB); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession abort(Transaction txn, IntroducerSession s, private IntroducerSession abort(Transaction txn,
Introducee remainingIntroducee) throws DbException { IntroducerSession s) 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 = long timestampA = getLocalTimestamp(s, s.getIntroduceeA());
getTimestampForInvisibleMessage(s, s.getIntroduceeA());
Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA); Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA);
long timestampB = long timestampB = getLocalTimestamp(s, s.getIntroduceeB());
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);
@@ -626,33 +589,9 @@ class IntroducerProtocolEngine
return isInvalidDependency(expected, dependency); return isInvalidDependency(expected, dependency);
} }
/** private long getLocalTimestamp(IntroducerSession s, PeerSession p) {
* Returns a timestamp for a visible outgoing message. The timestamp is return getLocalTimestamp(p.getLocalTimestamp(),
* later than the timestamp of any message sent or received so far in the s.getRequestTimestamp());
* 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

@@ -2,6 +2,9 @@ package org.briarproject.briar.introduction;
interface IntroductionConstants { interface IntroductionConstants {
// Group metadata keys
String GROUP_KEY_CONTACT_ID = "contactId";
// Message metadata keys // Message metadata keys
String MSG_KEY_MESSAGE_TYPE = "messageType"; String MSG_KEY_MESSAGE_TYPE = "messageType";
String MSG_KEY_SESSION_ID = "sessionId"; String MSG_KEY_SESSION_ID = "sessionId";
@@ -9,7 +12,6 @@ interface IntroductionConstants {
String MSG_KEY_LOCAL = "local"; String MSG_KEY_LOCAL = "local";
String MSG_KEY_VISIBLE_IN_UI = "visibleInUi"; String MSG_KEY_VISIBLE_IN_UI = "visibleInUi";
String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer"; String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer";
String MSG_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer";
// Session Keys // Session Keys
String SESSION_KEY_SESSION_ID = "sessionId"; String SESSION_KEY_SESSION_ID = "sessionId";

View File

@@ -59,6 +59,7 @@ import static org.briarproject.briar.introduction.IntroduceeState.REMOTE_DECLINE
import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED; import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED;
import static org.briarproject.briar.introduction.IntroducerState.B_DECLINED; import static org.briarproject.briar.introduction.IntroducerState.B_DECLINED;
import static org.briarproject.briar.introduction.IntroducerState.START; import static org.briarproject.briar.introduction.IntroducerState.START;
import static org.briarproject.briar.introduction.IntroductionConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.introduction.MessageType.ABORT; import static org.briarproject.briar.introduction.MessageType.ABORT;
import static org.briarproject.briar.introduction.MessageType.ACCEPT; import static org.briarproject.briar.introduction.MessageType.ACCEPT;
import static org.briarproject.briar.introduction.MessageType.ACTIVATE; import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
@@ -135,7 +136,13 @@ class IntroductionManagerImpl extends ConversationClientImpl
c.getId(), CLIENT_ID, MAJOR_VERSION); c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client); db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group // Attach the contact ID to the group
clientHelper.setContactId(txn, g.getId(), c.getId()); BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
try {
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
} }
@Override @Override
@@ -176,7 +183,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
} }
StoredSession ss = getSession(txn, sessionId); StoredSession ss = getSession(txn, sessionId);
// Handle the message // Handle the message
Session<?> session; Session session;
MessageId storageId; MessageId storageId;
if (ss == null) { if (ss == null) {
if (meta.getMessageType() != REQUEST) throw new FormatException(); if (meta.getMessageType() != REQUEST) throw new FormatException();
@@ -204,7 +211,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
private IntroduceeSession createNewIntroduceeSession(Transaction txn, private IntroduceeSession createNewIntroduceeSession(Transaction txn,
Message m, BdfList body) throws DbException, FormatException { Message m, BdfList body) throws DbException, FormatException {
ContactId introducerId = clientHelper.getContactId(txn, m.getGroupId()); ContactId introducerId = getContactId(txn, m.getGroupId());
Author introducer = db.getContact(txn, introducerId).getAuthor(); Author introducer = db.getContact(txn, introducerId).getAuthor();
Author local = identityManager.getLocalAuthor(txn); Author local = identityManager.getLocalAuthor(txn);
Author remote = messageParser.parseRequestMessage(m, body).getAuthor(); Author remote = messageParser.parseRequestMessage(m, body).getAuthor();
@@ -216,7 +223,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
remote); remote);
} }
private <S extends Session<?>> S handleMessage(Transaction txn, Message m, private <S extends Session> S handleMessage(Transaction txn, Message m,
BdfList body, MessageType type, S session, ProtocolEngine<S> engine) BdfList body, MessageType type, S session, ProtocolEngine<S> engine)
throws DbException, FormatException { throws DbException, FormatException {
if (type == REQUEST) { if (type == REQUEST) {
@@ -256,6 +263,13 @@ class IntroductionManagerImpl extends ConversationClientImpl
results.values().iterator().next()); results.values().iterator().next());
} }
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException, FormatException {
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, contactGroupId);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
}
private MessageId createStorageId(Transaction txn) throws DbException { private MessageId createStorageId(Transaction txn) throws DbException {
Message m = clientHelper Message m = clientHelper
.createMessageForStoringMetadata(localGroup.getId()); .createMessageForStoringMetadata(localGroup.getId());
@@ -264,7 +278,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
} }
private void storeSession(Transaction txn, MessageId storageId, private void storeSession(Transaction txn, MessageId storageId,
Session<?> session) throws DbException { Session session) throws DbException {
BdfDictionary d; BdfDictionary d;
if (session.getRole() == INTRODUCER) { if (session.getRole() == INTRODUCER) {
d = sessionEncoder d = sessionEncoder
@@ -311,8 +325,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,
throws DbException { long timestamp) 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,7 +358,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
storageId = ss.storageId; storageId = ss.storageId;
} }
// Handle the request action // Handle the request action
session = introducerEngine.onRequestAction(txn, session, text); session = introducerEngine
.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);
@@ -357,7 +372,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
@Override @Override
public void respondToIntroduction(ContactId contactId, SessionId sessionId, public void respondToIntroduction(ContactId contactId, SessionId sessionId,
boolean accept) throws DbException { long timestamp, boolean accept) throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// Look up the session // Look up the session
@@ -375,9 +390,11 @@ 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.onAcceptAction(txn, session); session = introduceeEngine
.onAcceptAction(txn, session, timestamp);
} else { } else {
session = introduceeEngine.onDeclineAction(txn, session); session = introduceeEngine
.onDeclineAction(txn, session, timestamp);
} }
// Store the updated session // Store the updated session
storeSession(txn, ss.storageId, session); storeSession(txn, ss.storageId, session);
@@ -444,7 +461,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
return new IntroductionRequest(m, contactGroupId, meta.getTimestamp(), return new IntroductionRequest(m, contactGroupId, meta.getTimestamp(),
meta.isLocal(), meta.isRead(), status.isSent(), status.isSeen(), meta.isLocal(), meta.isRead(), status.isSent(), status.isSeen(),
sessionId, author, text, !meta.isAvailableToAnswer(), sessionId, author, text, !meta.isAvailableToAnswer(),
authorInfo, rm.getAutoDeleteTimer()); authorInfo);
} }
private IntroductionResponse parseInvitationResponse(Transaction txn, private IntroductionResponse parseInvitationResponse(Transaction txn,
@@ -482,8 +499,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
} }
return new IntroductionResponse(m, contactGroupId, meta.getTimestamp(), return new IntroductionResponse(m, contactGroupId, meta.getTimestamp(),
meta.isLocal(), meta.isRead(), status.isSent(), status.isSeen(), meta.isLocal(), meta.isRead(), status.isSent(), status.isSeen(),
sessionId, accept, author, authorInfo, role, canSucceed, sessionId, accept, author, authorInfo, role, canSucceed);
meta.getAutoDeleteTimer());
} }
private void removeSessionWithIntroducer(Transaction txn, private void removeSessionWithIntroducer(Transaction txn,
@@ -649,7 +665,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
try { try {
StoredSession ss = getSession(txn, sessionId); StoredSession ss = getSession(txn, sessionId);
if (ss == null) throw new AssertionError(); if (ss == null) throw new AssertionError();
Session<?> s; Session s;
Role role = sessionParser.getRole(ss.bdfSession); Role role = sessionParser.getRole(ss.bdfSession);
if (role == INTRODUCER) { if (role == INTRODUCER) {
s = sessionParser.parseIntroducerSession(ss.bdfSession); s = sessionParser.parseIntroducerSession(ss.bdfSession);

View File

@@ -23,12 +23,10 @@ import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
import static org.briarproject.briar.introduction.MessageType.ACCEPT; import static org.briarproject.briar.introduction.MessageType.ACCEPT;
import static org.briarproject.briar.introduction.MessageType.ACTIVATE; import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
import static org.briarproject.briar.introduction.MessageType.AUTH; import static org.briarproject.briar.introduction.MessageType.AUTH;
import static org.briarproject.briar.util.ValidationUtils.validateAutoDeleteTimer;
@Immutable @Immutable
@@ -54,14 +52,13 @@ class IntroductionValidator extends BdfMessageValidator {
return validateRequestMessage(m, body); return validateRequestMessage(m, body);
case ACCEPT: case ACCEPT:
return validateAcceptMessage(m, body); return validateAcceptMessage(m, body);
case DECLINE:
return validateDeclineMessage(type, m, body);
case AUTH: case AUTH:
return validateAuthMessage(m, body); return validateAuthMessage(m, body);
case ACTIVATE: case ACTIVATE:
return validateActivateMessage(m, body); return validateActivateMessage(m, body);
case DECLINE:
case ABORT: case ABORT:
return validateAbortMessage(type, m, body); return validateOtherMessage(type, m, body);
default: default:
throw new FormatException(); throw new FormatException();
} }
@@ -69,11 +66,7 @@ class IntroductionValidator extends BdfMessageValidator {
private BdfMessageContext validateRequestMessage(Message m, BdfList body) private BdfMessageContext validateRequestMessage(Message m, BdfList body)
throws FormatException { throws FormatException {
// Client version 0.0: Message type, optional previous message ID, checkSize(body, 4);
// author, optional text.
// Client version 0.1: Message type, optional previous message ID,
// author, optional text, optional auto-delete timer.
checkSize(body, 4, 5);
byte[] previousMessageId = body.getOptionalRaw(1); byte[] previousMessageId = body.getOptionalRaw(1);
checkLength(previousMessageId, UniqueId.LENGTH); checkLength(previousMessageId, UniqueId.LENGTH);
@@ -84,13 +77,8 @@ class IntroductionValidator extends BdfMessageValidator {
String text = body.getOptionalString(3); String text = body.getOptionalString(3);
checkLength(text, 1, MAX_INTRODUCTION_TEXT_LENGTH); checkLength(text, 1, MAX_INTRODUCTION_TEXT_LENGTH);
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 5) {
timer = validateAutoDeleteTimer(body.getOptionalLong(4));
}
BdfDictionary meta = BdfDictionary meta =
messageEncoder.encodeRequestMetadata(m.getTimestamp(), timer); messageEncoder.encodeRequestMetadata(m.getTimestamp());
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -101,12 +89,7 @@ class IntroductionValidator extends BdfMessageValidator {
private BdfMessageContext validateAcceptMessage(Message m, BdfList body) private BdfMessageContext validateAcceptMessage(Message m, BdfList body)
throws FormatException { throws FormatException {
// Client version 0.0: Message type, session ID, optional previous checkSize(body, 6);
// message ID, ephemeral public key, timestamp, transport properties.
// Client version 0.1: Message type, session ID, optional previous
// message ID, ephemeral public key, timestamp, transport properties,
// optional auto-delete timer.
checkSize(body, 6, 7);
byte[] sessionIdBytes = body.getRaw(1); byte[] sessionIdBytes = body.getRaw(1);
checkLength(sessionIdBytes, UniqueId.LENGTH); checkLength(sessionIdBytes, UniqueId.LENGTH);
@@ -126,44 +109,9 @@ class IntroductionValidator extends BdfMessageValidator {
clientHelper clientHelper
.parseAndValidateTransportPropertiesMap(transportProperties); .parseAndValidateTransportPropertiesMap(transportProperties);
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 7) {
timer = validateAutoDeleteTimer(body.getOptionalLong(6));
}
SessionId sessionId = new SessionId(sessionIdBytes); SessionId sessionId = new SessionId(sessionIdBytes);
BdfDictionary meta = messageEncoder.encodeMetadata(ACCEPT, sessionId, BdfDictionary meta = messageEncoder.encodeMetadata(ACCEPT, sessionId,
m.getTimestamp(), false, false, false, timer); m.getTimestamp(), false, false, false);
if (previousMessageId == null) {
return new BdfMessageContext(meta);
} else {
MessageId dependency = new MessageId(previousMessageId);
return new BdfMessageContext(meta, singletonList(dependency));
}
}
private BdfMessageContext validateDeclineMessage(MessageType type,
Message m, BdfList body) throws FormatException {
// Client version 0.0: Message type, session ID, optional previous
// message ID.
// Client version 0.1: Message type, session ID, optional previous
// message ID, optional auto-delete timer.
checkSize(body, 3, 4);
byte[] sessionIdBytes = body.getRaw(1);
checkLength(sessionIdBytes, UniqueId.LENGTH);
byte[] previousMessageId = body.getOptionalRaw(2);
checkLength(previousMessageId, UniqueId.LENGTH);
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 4) {
timer = validateAutoDeleteTimer(body.getOptionalLong(3));
}
SessionId sessionId = new SessionId(sessionIdBytes);
BdfDictionary meta = messageEncoder.encodeMetadata(type, sessionId,
m.getTimestamp(), false, false, false, timer);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -190,7 +138,7 @@ class IntroductionValidator extends BdfMessageValidator {
SessionId sessionId = new SessionId(sessionIdBytes); SessionId sessionId = new SessionId(sessionIdBytes);
BdfDictionary meta = messageEncoder.encodeMetadata(AUTH, sessionId, BdfDictionary meta = messageEncoder.encodeMetadata(AUTH, sessionId,
m.getTimestamp(), false, false, false, NO_AUTO_DELETE_TIMER); m.getTimestamp(), false, false, false);
MessageId dependency = new MessageId(previousMessageId); MessageId dependency = new MessageId(previousMessageId);
return new BdfMessageContext(meta, singletonList(dependency)); return new BdfMessageContext(meta, singletonList(dependency));
} }
@@ -210,7 +158,7 @@ class IntroductionValidator extends BdfMessageValidator {
SessionId sessionId = new SessionId(sessionIdBytes); SessionId sessionId = new SessionId(sessionIdBytes);
BdfDictionary meta = messageEncoder.encodeMetadata(ACTIVATE, sessionId, BdfDictionary meta = messageEncoder.encodeMetadata(ACTIVATE, sessionId,
m.getTimestamp(), false, false, false, NO_AUTO_DELETE_TIMER); m.getTimestamp(), false, false, false);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -219,7 +167,7 @@ class IntroductionValidator extends BdfMessageValidator {
} }
} }
private BdfMessageContext validateAbortMessage(MessageType type, private BdfMessageContext validateOtherMessage(MessageType type,
Message m, BdfList body) throws FormatException { Message m, BdfList body) throws FormatException {
checkSize(body, 3); checkSize(body, 3);
@@ -231,7 +179,7 @@ class IntroductionValidator extends BdfMessageValidator {
SessionId sessionId = new SessionId(sessionIdBytes); SessionId sessionId = new SessionId(sessionIdBytes);
BdfDictionary meta = messageEncoder.encodeMetadata(type, sessionId, BdfDictionary meta = messageEncoder.encodeMetadata(type, sessionId,
m.getTimestamp(), false, false, false, NO_AUTO_DELETE_TIMER); m.getTimestamp(), false, false, false);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -239,4 +187,5 @@ class IntroductionValidator extends BdfMessageValidator {
return new BdfMessageContext(meta, singletonList(dependency)); return new BdfMessageContext(meta, singletonList(dependency));
} }
} }
} }

View File

@@ -18,12 +18,11 @@ import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
interface MessageEncoder { interface MessageEncoder {
BdfDictionary encodeRequestMetadata(long timestamp, BdfDictionary encodeRequestMetadata(long timestamp);
long autoDeleteTimer);
BdfDictionary encodeMetadata(MessageType type, BdfDictionary encodeMetadata(MessageType type,
@Nullable SessionId sessionId, long timestamp, boolean local, @Nullable SessionId sessionId, long timestamp, boolean local,
boolean read, boolean visible, long autoDeleteTimer); boolean read, boolean visible);
void addSessionId(BdfDictionary meta, SessionId sessionId); void addSessionId(BdfDictionary meta, SessionId sessionId);
@@ -31,53 +30,18 @@ interface MessageEncoder {
void setAvailableToAnswer(BdfDictionary meta, boolean available); void setAvailableToAnswer(BdfDictionary meta, boolean available);
/**
* Encodes a request message without an auto-delete timer.
*/
Message encodeRequestMessage(GroupId contactGroupId, long timestamp, Message encodeRequestMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, Author author, @Nullable MessageId previousMessageId, Author author,
@Nullable String text); @Nullable String text);
/**
* Encodes a request message with an optional auto-delete timer. This
* requires the contact to support client version 0.1 or higher.
*/
Message encodeRequestMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, Author author,
@Nullable String text, long autoDeleteTimer);
/**
* Encodes an accept message without an auto-delete timer.
*/
Message encodeAcceptMessage(GroupId contactGroupId, long timestamp, Message encodeAcceptMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId, @Nullable MessageId previousMessageId, SessionId sessionId,
PublicKey ephemeralPublicKey, long acceptTimestamp, PublicKey ephemeralPublicKey, long acceptTimestamp,
Map<TransportId, TransportProperties> transportProperties); Map<TransportId, TransportProperties> transportProperties);
/**
* Encodes an accept message with an optional auto-delete timer. This
* requires the contact to support client version 0.1 or higher.
*/
Message encodeAcceptMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId,
PublicKey ephemeralPublicKey, long acceptTimestamp,
Map<TransportId, TransportProperties> transportProperties,
long autoDeleteTimer);
/**
* Encodes a decline message without an auto-delete timer.
*/
Message encodeDeclineMessage(GroupId contactGroupId, long timestamp, Message encodeDeclineMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId); @Nullable MessageId previousMessageId, SessionId sessionId);
/**
* Encodes a decline message with an optional auto-delete timer. This
* requires the contact to support client version 0.1 or higher.
*/
Message encodeDeclineMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId,
long autoDeleteTimer);
Message encodeAuthMessage(GroupId contactGroupId, long timestamp, Message encodeAuthMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId, @Nullable MessageId previousMessageId, SessionId sessionId,
byte[] mac, byte[] signature); byte[] mac, byte[] signature);

View File

@@ -20,9 +20,7 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
@@ -50,10 +48,9 @@ class MessageEncoderImpl implements MessageEncoder {
} }
@Override @Override
public BdfDictionary encodeRequestMetadata(long timestamp, public BdfDictionary encodeRequestMetadata(long timestamp) {
long autoDeleteTimer) { BdfDictionary meta =
BdfDictionary meta = encodeMetadata(REQUEST, null, timestamp, encodeMetadata(REQUEST, null, timestamp, false, false, false);
false, false, false, autoDeleteTimer);
meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, false); meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, false);
return meta; return meta;
} }
@@ -61,7 +58,7 @@ class MessageEncoderImpl implements MessageEncoder {
@Override @Override
public BdfDictionary encodeMetadata(MessageType type, public BdfDictionary encodeMetadata(MessageType type,
@Nullable SessionId sessionId, long timestamp, boolean local, @Nullable SessionId sessionId, long timestamp, boolean local,
boolean read, boolean visible, long autoDeleteTimer) { boolean read, boolean visible) {
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue()); meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue());
if (sessionId != null) if (sessionId != null)
@@ -72,9 +69,6 @@ class MessageEncoderImpl implements MessageEncoder {
meta.put(MSG_KEY_LOCAL, local); meta.put(MSG_KEY_LOCAL, local);
meta.put(MSG_KEY_READ, read); meta.put(MSG_KEY_READ, read);
meta.put(MSG_KEY_VISIBLE_IN_UI, visible); meta.put(MSG_KEY_VISIBLE_IN_UI, visible);
if (autoDeleteTimer != NO_AUTO_DELETE_TIMER) {
meta.put(MSG_KEY_AUTO_DELETE_TIMER, autoDeleteTimer);
}
return meta; return meta;
} }
@@ -109,23 +103,6 @@ class MessageEncoderImpl implements MessageEncoder {
return createMessage(contactGroupId, timestamp, body); return createMessage(contactGroupId, timestamp, body);
} }
@Override
public Message encodeRequestMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, Author author,
@Nullable String text, long autoDeleteTimer) {
if (text != null && text.isEmpty()) {
throw new IllegalArgumentException();
}
BdfList body = BdfList.of(
REQUEST.getValue(),
previousMessageId,
clientHelper.toList(author),
text,
encodeTimer(autoDeleteTimer)
);
return createMessage(contactGroupId, timestamp, body);
}
@Override @Override
public Message encodeAcceptMessage(GroupId contactGroupId, long timestamp, public Message encodeAcceptMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId, @Nullable MessageId previousMessageId, SessionId sessionId,
@@ -142,46 +119,11 @@ class MessageEncoderImpl implements MessageEncoder {
return createMessage(contactGroupId, timestamp, body); return createMessage(contactGroupId, timestamp, body);
} }
@Override
public Message encodeAcceptMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId,
PublicKey ephemeralPublicKey, long acceptTimestamp,
Map<TransportId, TransportProperties> transportProperties,
long autoDeleteTimer) {
BdfList body = BdfList.of(
ACCEPT.getValue(),
sessionId,
previousMessageId,
ephemeralPublicKey.getEncoded(),
acceptTimestamp,
clientHelper.toDictionary(transportProperties),
encodeTimer(autoDeleteTimer)
);
return createMessage(contactGroupId, timestamp, body);
}
@Override @Override
public Message encodeDeclineMessage(GroupId contactGroupId, long timestamp, public Message encodeDeclineMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId) { @Nullable MessageId previousMessageId, SessionId sessionId) {
BdfList body = BdfList.of( return encodeMessage(DECLINE, contactGroupId, sessionId, timestamp,
DECLINE.getValue(), previousMessageId);
sessionId,
previousMessageId
);
return createMessage(contactGroupId, timestamp, body);
}
@Override
public Message encodeDeclineMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId,
long autoDeleteTimer) {
BdfList body = BdfList.of(
DECLINE.getValue(),
sessionId,
previousMessageId,
encodeTimer(autoDeleteTimer)
);
return createMessage(contactGroupId, timestamp, body);
} }
@Override @Override
@@ -214,8 +156,15 @@ class MessageEncoderImpl implements MessageEncoder {
@Override @Override
public Message encodeAbortMessage(GroupId contactGroupId, long timestamp, public Message encodeAbortMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId) { @Nullable MessageId previousMessageId, SessionId sessionId) {
return encodeMessage(ABORT, contactGroupId, sessionId, timestamp,
previousMessageId);
}
private Message encodeMessage(MessageType type, GroupId contactGroupId,
SessionId sessionId, long timestamp,
@Nullable MessageId previousMessageId) {
BdfList body = BdfList.of( BdfList body = BdfList.of(
ABORT.getValue(), type.getValue(),
sessionId, sessionId,
previousMessageId previousMessageId
); );
@@ -232,8 +181,4 @@ class MessageEncoderImpl implements MessageEncoder {
} }
} }
@Nullable
private Long encodeTimer(long autoDeleteTimer) {
return autoDeleteTimer == NO_AUTO_DELETE_TIMER ? null : autoDeleteTimer;
}
} }

View File

@@ -13,12 +13,12 @@ class MessageMetadata {
private final MessageType type; private final MessageType type;
@Nullable @Nullable
private final SessionId sessionId; private final SessionId sessionId;
private final long timestamp, autoDeleteTimer; private final long timestamp;
private final boolean local, read, visible, available; private final boolean local, read, visible, available;
MessageMetadata(MessageType type, @Nullable SessionId sessionId, MessageMetadata(MessageType type, @Nullable SessionId sessionId,
long timestamp, boolean local, boolean read, boolean visible, long timestamp, boolean local, boolean read, boolean visible,
boolean available, long autoDeleteTimer) { boolean available) {
this.type = type; this.type = type;
this.sessionId = sessionId; this.sessionId = sessionId;
this.timestamp = timestamp; this.timestamp = timestamp;
@@ -26,7 +26,6 @@ class MessageMetadata {
this.read = read; this.read = read;
this.visible = visible; this.visible = visible;
this.available = available; this.available = available;
this.autoDeleteTimer = autoDeleteTimer;
} }
MessageType getMessageType() { MessageType getMessageType() {
@@ -58,7 +57,4 @@ class MessageMetadata {
return available; return available;
} }
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
} }

View File

@@ -19,9 +19,7 @@ import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
@@ -67,9 +65,8 @@ class MessageParserImpl implements MessageParser {
boolean read = d.getBoolean(MSG_KEY_READ); boolean read = d.getBoolean(MSG_KEY_READ);
boolean visible = d.getBoolean(MSG_KEY_VISIBLE_IN_UI); boolean visible = d.getBoolean(MSG_KEY_VISIBLE_IN_UI);
boolean available = d.getBoolean(MSG_KEY_AVAILABLE_TO_ANSWER, false); boolean available = d.getBoolean(MSG_KEY_AVAILABLE_TO_ANSWER, false);
long timer = d.getLong(MSG_KEY_AUTO_DELETE_TIMER, NO_AUTO_DELETE_TIMER);
return new MessageMetadata(type, sessionId, timestamp, local, read, return new MessageMetadata(type, sessionId, timestamp, local, read,
visible, available, timer); visible, available);
} }
@Override @Override
@@ -80,10 +77,8 @@ class MessageParserImpl implements MessageParser {
new MessageId(previousMsgBytes)); new MessageId(previousMsgBytes));
Author author = clientHelper.parseAndValidateAuthor(body.getList(2)); Author author = clientHelper.parseAndValidateAuthor(body.getList(2));
String text = body.getOptionalString(3); String text = body.getOptionalString(3);
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 5) timer = body.getLong(4, NO_AUTO_DELETE_TIMER);
return new RequestMessage(m.getId(), m.getGroupId(), return new RequestMessage(m.getId(), m.getGroupId(),
m.getTimestamp(), previousMessageId, author, text, timer); m.getTimestamp(), previousMessageId, author, text);
} }
@Override @Override
@@ -97,11 +92,9 @@ class MessageParserImpl implements MessageParser {
long acceptTimestamp = body.getLong(4); long acceptTimestamp = body.getLong(4);
Map<TransportId, TransportProperties> transportProperties = clientHelper Map<TransportId, TransportProperties> transportProperties = clientHelper
.parseAndValidateTransportPropertiesMap(body.getDictionary(5)); .parseAndValidateTransportPropertiesMap(body.getDictionary(5));
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 7) timer = body.getLong(6, NO_AUTO_DELETE_TIMER);
return new AcceptMessage(m.getId(), m.getGroupId(), m.getTimestamp(), return new AcceptMessage(m.getId(), m.getGroupId(), m.getTimestamp(),
previousMessageId, sessionId, ephemeralPublicKey, previousMessageId, sessionId, ephemeralPublicKey,
acceptTimestamp, transportProperties, timer); acceptTimestamp, transportProperties);
} }
@Override @Override
@@ -111,10 +104,8 @@ class MessageParserImpl implements MessageParser {
byte[] previousMsgBytes = body.getOptionalRaw(2); byte[] previousMsgBytes = body.getOptionalRaw(2);
MessageId previousMessageId = (previousMsgBytes == null ? null : MessageId previousMessageId = (previousMsgBytes == null ? null :
new MessageId(previousMsgBytes)); new MessageId(previousMsgBytes));
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 4) timer = body.getLong(3, NO_AUTO_DELETE_TIMER);
return new DeclineMessage(m.getId(), m.getGroupId(), m.getTimestamp(), return new DeclineMessage(m.getId(), m.getGroupId(), m.getTimestamp(),
previousMessageId, sessionId, timer); previousMessageId, sessionId);
} }
@Override @Override

View File

@@ -8,14 +8,16 @@ 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 onAcceptAction(Transaction txn, S session) throws DbException; S onDeclineAction(Transaction txn, S session, long timestamp)
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

@@ -18,9 +18,8 @@ class RequestMessage extends AbstractIntroductionMessage {
RequestMessage(MessageId messageId, GroupId groupId, long timestamp, RequestMessage(MessageId messageId, GroupId groupId, long timestamp,
@Nullable MessageId previousMessageId, Author author, @Nullable MessageId previousMessageId, Author author,
@Nullable String text, long autoDeleteTimer) { @Nullable String text) {
super(messageId, groupId, timestamp, previousMessageId, super(messageId, groupId, timestamp, previousMessageId);
autoDeleteTimer);
this.author = author; this.author = author;
this.text = text; this.text = text;
} }

View File

@@ -6,7 +6,6 @@ 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;
@@ -21,20 +20,16 @@ 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, Clock clock) { ConversationManagerImpl(DatabaseComponent db) {
this.db = db; this.db = db;
this.clock = clock;
clients = new CopyOnWriteArraySet<>(); clients = new CopyOnWriteArraySet<>();
} }
@@ -62,33 +57,24 @@ 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;
for (ConversationClient client : clients) { Transaction txn = db.startTransaction(true);
GroupCount count = client.getGroupCount(txn, contactId); try {
msgCount += count.getMsgCount(); for (ConversationClient client : clients) {
unreadCount += count.getUnreadCount(); GroupCount count = client.getGroupCount(txn, contactId);
if (count.getLatestMsgTime() > latestTime) msgCount += count.getMsgCount();
latestTime = count.getLatestMsgTime(); unreadCount += count.getUnreadCount();
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 -> {
@@ -101,8 +87,8 @@ class ConversationManagerImpl implements ConversationManager {
} }
@Override @Override
public DeletionResult deleteMessages(ContactId c, public DeletionResult deleteMessages(ContactId c, Collection<MessageId> toDelete)
Collection<MessageId> toDelete) throws DbException { 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

@@ -2,6 +2,9 @@ package org.briarproject.briar.messaging;
interface MessagingConstants { interface MessagingConstants {
// Metadata keys for groups
String GROUP_KEY_CONTACT_ID = "contactId";
// Metadata keys for messages // Metadata keys for messages
String MSG_KEY_TIMESTAMP = "timestamp"; String MSG_KEY_TIMESTAMP = "timestamp";
String MSG_KEY_LOCAL = "local"; String MSG_KEY_LOCAL = "local";
@@ -10,5 +13,4 @@ interface MessagingConstants {
String MSG_KEY_DESCRIPTOR_LENGTH = "descriptorLength"; String MSG_KEY_DESCRIPTOR_LENGTH = "descriptorLength";
String MSG_KEY_HAS_TEXT = "hasText"; String MSG_KEY_HAS_TEXT = "hasText";
String MSG_KEY_ATTACHMENT_HEADERS = "attachmentHeaders"; String MSG_KEY_ATTACHMENT_HEADERS = "attachmentHeaders";
String MSG_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer";
} }

View File

@@ -27,7 +27,6 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook; import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook; import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient; import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient;
@@ -39,7 +38,6 @@ import org.briarproject.briar.api.messaging.FileTooBigException;
import org.briarproject.briar.api.messaging.InvalidAttachmentException; import org.briarproject.briar.api.messaging.InvalidAttachmentException;
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;
import org.briarproject.briar.api.messaging.PrivateMessageFormat;
import org.briarproject.briar.api.messaging.PrivateMessageHeader; import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent; import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent; import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
@@ -62,15 +60,11 @@ import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED; import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES_AUTO_DELETE;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_ONLY;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT; import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE; import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE;
import static org.briarproject.briar.messaging.MessagingConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_ATTACHMENT_HEADERS; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_ATTACHMENT_HEADERS;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_CONTENT_TYPE; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_CONTENT_TYPE;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_DESCRIPTOR_LENGTH; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_HAS_TEXT; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_HAS_TEXT;
@@ -90,24 +84,18 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
private final MessageTracker messageTracker; private final MessageTracker messageTracker;
private final ClientVersioningManager clientVersioningManager; private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final AutoDeleteManager autoDeleteManager;
@Inject @Inject
MessagingManagerImpl( MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
DatabaseComponent db,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MetadataParser metadataParser, MessageTracker messageTracker,
MessageTracker messageTracker, ContactGroupFactory contactGroupFactory) {
ContactGroupFactory contactGroupFactory,
AutoDeleteManager autoDeleteManager) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.metadataParser = metadataParser; this.metadataParser = metadataParser;
this.messageTracker = messageTracker; this.messageTracker = messageTracker;
this.clientVersioningManager = clientVersioningManager; this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.autoDeleteManager = autoDeleteManager;
} }
@Override @Override
@@ -145,7 +133,13 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
c.getId(), CLIENT_ID, MAJOR_VERSION); c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client); db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group // Attach the contact ID to the group
clientHelper.setContactId(txn, g.getId(), c.getId()); BdfDictionary d = new BdfDictionary();
d.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
try {
clientHelper.mergeGroupMetadata(txn, g.getId(), d);
} catch (FormatException e) {
throw new AssertionError(e);
}
// Initialize the group count with current time // Initialize the group count with current time
messageTracker.initializeGroupCount(txn, g.getId()); messageTracker.initializeGroupCount(txn, g.getId());
} }
@@ -202,18 +196,14 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
long timestamp = meta.getLong(MSG_KEY_TIMESTAMP); long timestamp = meta.getLong(MSG_KEY_TIMESTAMP);
boolean local = meta.getBoolean(MSG_KEY_LOCAL); boolean local = meta.getBoolean(MSG_KEY_LOCAL);
boolean read = meta.getBoolean(MSG_KEY_READ); boolean read = meta.getBoolean(MSG_KEY_READ);
long timer = meta.getLong(MSG_KEY_AUTO_DELETE_TIMER,
NO_AUTO_DELETE_TIMER);
PrivateMessageHeader header = PrivateMessageHeader header =
new PrivateMessageHeader(m.getId(), groupId, timestamp, local, new PrivateMessageHeader(m.getId(), groupId, timestamp, local,
read, false, false, hasText, headers, timer); read, false, false, hasText, headers);
ContactId contactId = getContactId(txn, groupId); ContactId contactId = getContactId(txn, groupId);
PrivateMessageReceivedEvent event = PrivateMessageReceivedEvent event =
new PrivateMessageReceivedEvent(header, contactId); new PrivateMessageReceivedEvent(header, contactId);
txn.attach(event); txn.attach(event);
messageTracker.trackIncomingMessage(txn, m); messageTracker.trackIncomingMessage(txn, m);
autoDeleteManager.receiveAutoDeleteTimer(txn, contactId, timer,
timestamp);
} }
private List<AttachmentHeader> parseAttachmentHeaders(BdfDictionary meta) private List<AttachmentHeader> parseAttachmentHeaders(BdfDictionary meta)
@@ -238,18 +228,13 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
@Override @Override
public void addLocalMessage(PrivateMessage m) throws DbException { public void addLocalMessage(PrivateMessage m) throws DbException {
db.transaction(false, txn -> addLocalMessage(txn, m)); Transaction txn = db.startTransaction(false);
}
@Override
public void addLocalMessage(Transaction txn, PrivateMessage m)
throws DbException {
try { try {
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_TIMESTAMP, m.getMessage().getTimestamp()); meta.put(MSG_KEY_TIMESTAMP, m.getMessage().getTimestamp());
meta.put(MSG_KEY_LOCAL, true); meta.put(MSG_KEY_LOCAL, true);
meta.put(MSG_KEY_READ, true); meta.put(MSG_KEY_READ, true);
if (m.getFormat() != TEXT_ONLY) { if (!m.isLegacyFormat()) {
meta.put(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE); meta.put(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE);
meta.put(MSG_KEY_HAS_TEXT, m.hasText()); meta.put(MSG_KEY_HAS_TEXT, m.hasText());
BdfList headers = new BdfList(); BdfList headers = new BdfList();
@@ -258,12 +243,6 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
BdfList.of(a.getMessageId(), a.getContentType())); BdfList.of(a.getMessageId(), a.getContentType()));
} }
meta.put(MSG_KEY_ATTACHMENT_HEADERS, headers); meta.put(MSG_KEY_ATTACHMENT_HEADERS, headers);
if (m.getFormat() == TEXT_IMAGES_AUTO_DELETE) {
long timer = m.getAutoDeleteTimer();
if (timer != NO_AUTO_DELETE_TIMER) {
meta.put(MSG_KEY_AUTO_DELETE_TIMER, timer);
}
}
} }
// Mark attachments as shared and permanent now we're ready to send // Mark attachments as shared and permanent now we're ready to send
for (AttachmentHeader a : m.getAttachmentHeaders()) { for (AttachmentHeader a : m.getAttachmentHeaders()) {
@@ -273,8 +252,11 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true, clientHelper.addLocalMessage(txn, m.getMessage(), meta, true,
false); false);
messageTracker.trackOutgoingMessage(txn, m.getMessage()); messageTracker.trackOutgoingMessage(txn, m.getMessage());
db.commitTransaction(txn);
} catch (FormatException e) { } catch (FormatException e) {
throw new AssertionError(e); throw new AssertionError(e);
} finally {
db.endTransaction(txn);
} }
} }
@@ -373,14 +355,12 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
if (messageType == null) { if (messageType == null) {
headers.add(new PrivateMessageHeader(id, g, timestamp, headers.add(new PrivateMessageHeader(id, g, timestamp,
local, read, s.isSent(), s.isSeen(), true, local, read, s.isSent(), s.isSeen(), true,
emptyList(), NO_AUTO_DELETE_TIMER)); emptyList()));
} else { } else {
boolean hasText = meta.getBoolean(MSG_KEY_HAS_TEXT); boolean hasText = meta.getBoolean(MSG_KEY_HAS_TEXT);
long timer = meta.getLong(MSG_KEY_AUTO_DELETE_TIMER,
NO_AUTO_DELETE_TIMER);
headers.add(new PrivateMessageHeader(id, g, timestamp, headers.add(new PrivateMessageHeader(id, g, timestamp,
local, read, s.isSent(), s.isSeen(), hasText, local, read, s.isSent(), s.isSeen(), hasText,
parseAttachmentHeaders(meta), timer)); parseAttachmentHeaders(meta)));
} }
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
@@ -442,13 +422,12 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
} }
@Override @Override
public PrivateMessageFormat getContactMessageFormat(Transaction txn, public boolean contactSupportsImages(Transaction txn, ContactId c)
ContactId c) throws DbException { throws DbException {
int minorVersion = clientVersioningManager int minorVersion = clientVersioningManager
.getClientMinorVersion(txn, c, CLIENT_ID, 0); .getClientMinorVersion(txn, c, CLIENT_ID, 0);
if (minorVersion >= 3) return TEXT_IMAGES_AUTO_DELETE; // support was added in 0.1
else if (minorVersion >= 1) return TEXT_IMAGES; return minorVersion > 0;
else return TEXT_ONLY;
} }
@Override @Override

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.messaging; package org.briarproject.briar.messaging;
import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfReaderFactory; import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
@@ -19,7 +20,6 @@ import dagger.Provides;
import static org.briarproject.briar.api.messaging.MessagingManager.CLIENT_ID; import static org.briarproject.briar.api.messaging.MessagingManager.CLIENT_ID;
import static org.briarproject.briar.api.messaging.MessagingManager.MAJOR_VERSION; import static org.briarproject.briar.api.messaging.MessagingManager.MAJOR_VERSION;
import static org.briarproject.briar.api.messaging.MessagingManager.MINOR_VERSION;
@Module @Module
public class MessagingModule { public class MessagingModule {
@@ -57,14 +57,17 @@ public class MessagingModule {
ContactManager contactManager, ValidationManager validationManager, ContactManager contactManager, ValidationManager validationManager,
ConversationManager conversationManager, ConversationManager conversationManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
MessagingManagerImpl messagingManager) { FeatureFlags featureFlags, MessagingManagerImpl messagingManager) {
lifecycleManager.registerOpenDatabaseHook(messagingManager); lifecycleManager.registerOpenDatabaseHook(messagingManager);
contactManager.registerContactHook(messagingManager); contactManager.registerContactHook(messagingManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION, validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
messagingManager); messagingManager);
conversationManager.registerConversationClient(messagingManager); conversationManager.registerConversationClient(messagingManager);
// Advertise the current or previous minor version depending on the
// feature flag
int minorVersion = featureFlags.shouldEnableImageAttachments() ? 2 : 0;
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION, clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, messagingManager); minorVersion, messagingManager);
return messagingManager; return messagingManager;
} }

View File

@@ -17,7 +17,6 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong; import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE; import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE;
@@ -48,43 +47,21 @@ class PrivateMessageFactoryImpl implements PrivateMessageFactory {
public PrivateMessage createPrivateMessage(GroupId groupId, long timestamp, public PrivateMessage createPrivateMessage(GroupId groupId, long timestamp,
@Nullable String text, List<AttachmentHeader> headers) @Nullable String text, List<AttachmentHeader> headers)
throws FormatException { throws FormatException {
validateTextAndAttachmentHeaders(text, headers); // Validate the arguments
BdfList attachmentList = serialiseAttachmentHeaders(headers);
// Serialise the message
BdfList body = BdfList.of(PRIVATE_MESSAGE, text, attachmentList);
Message m = clientHelper.createMessage(groupId, timestamp, body);
return new PrivateMessage(m, text != null, headers);
}
@Override
public PrivateMessage createPrivateMessage(GroupId groupId, long timestamp,
@Nullable String text, List<AttachmentHeader> headers,
long autoDeleteTimer) throws FormatException {
validateTextAndAttachmentHeaders(text, headers);
BdfList attachmentList = serialiseAttachmentHeaders(headers);
// Serialise the message
Long timer = autoDeleteTimer == NO_AUTO_DELETE_TIMER ?
null : autoDeleteTimer;
BdfList body = BdfList.of(PRIVATE_MESSAGE, text, attachmentList, timer);
Message m = clientHelper.createMessage(groupId, timestamp, body);
return new PrivateMessage(m, text != null, headers, autoDeleteTimer);
}
private void validateTextAndAttachmentHeaders(@Nullable String text,
List<AttachmentHeader> headers) {
if (text == null) { if (text == null) {
if (headers.isEmpty()) throw new IllegalArgumentException(); if (headers.isEmpty()) throw new IllegalArgumentException();
} else if (utf8IsTooLong(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH)) { } else if (utf8IsTooLong(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH)) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
} // Serialise the attachment headers
private BdfList serialiseAttachmentHeaders(List<AttachmentHeader> headers) {
BdfList attachmentList = new BdfList(); BdfList attachmentList = new BdfList();
for (AttachmentHeader a : headers) { for (AttachmentHeader a : headers) {
attachmentList.add( attachmentList.add(
BdfList.of(a.getMessageId(), a.getContentType())); BdfList.of(a.getMessageId(), a.getContentType()));
} }
return attachmentList; // Serialise the message
BdfList body = BdfList.of(PRIVATE_MESSAGE, text, attachmentList);
Message m = clientHelper.createMessage(groupId, timestamp, body);
return new PrivateMessage(m, text != null, headers);
} }
} }

View File

@@ -27,7 +27,6 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_L
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
@@ -35,14 +34,12 @@ import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT; import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE; import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_ATTACHMENT_HEADERS; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_ATTACHMENT_HEADERS;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_CONTENT_TYPE; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_CONTENT_TYPE;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_DESCRIPTOR_LENGTH; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_HAS_TEXT; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_HAS_TEXT;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_MSG_TYPE; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_MSG_TYPE;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_TIMESTAMP; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_TIMESTAMP;
import static org.briarproject.briar.util.ValidationUtils.validateAutoDeleteTimer;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -102,7 +99,7 @@ class PrivateMessageValidator implements MessageValidator {
private BdfMessageContext validateLegacyPrivateMessage(Message m, private BdfMessageContext validateLegacyPrivateMessage(Message m,
BdfList body) throws FormatException { BdfList body) throws FormatException {
// Client version 0.0: Private message text // Private message text
checkSize(body, 1); checkSize(body, 1);
String text = body.getString(0); String text = body.getString(0);
checkLength(text, 0, MAX_PRIVATE_MESSAGE_TEXT_LENGTH); checkLength(text, 0, MAX_PRIVATE_MESSAGE_TEXT_LENGTH);
@@ -116,11 +113,8 @@ class PrivateMessageValidator implements MessageValidator {
private BdfMessageContext validatePrivateMessage(Message m, BdfList body) private BdfMessageContext validatePrivateMessage(Message m, BdfList body)
throws FormatException { throws FormatException {
// Client version 0.1 to 0.2: Message type, optional private message // Message type, optional private message text, attachment headers
// text, attachment headers. checkSize(body, 3);
// Client version 0.3: Message type, optional private message text,
// attachment headers, optional auto-delete timer.
checkSize(body, 3, 4);
String text = body.getOptionalString(1); String text = body.getOptionalString(1);
checkLength(text, 0, MAX_PRIVATE_MESSAGE_TEXT_LENGTH); checkLength(text, 0, MAX_PRIVATE_MESSAGE_TEXT_LENGTH);
BdfList headers = body.getList(2); BdfList headers = body.getList(2);
@@ -135,10 +129,6 @@ class PrivateMessageValidator implements MessageValidator {
String contentType = header.getString(1); String contentType = header.getString(1);
checkLength(contentType, 1, MAX_CONTENT_TYPE_BYTES); checkLength(contentType, 1, MAX_CONTENT_TYPE_BYTES);
} }
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 4) {
timer = validateAutoDeleteTimer(body.getOptionalLong(3));
}
// Return the metadata // Return the metadata
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_TIMESTAMP, m.getTimestamp()); meta.put(MSG_KEY_TIMESTAMP, m.getTimestamp());
@@ -147,9 +137,6 @@ class PrivateMessageValidator implements MessageValidator {
meta.put(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE); meta.put(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE);
meta.put(MSG_KEY_HAS_TEXT, text != null); meta.put(MSG_KEY_HAS_TEXT, text != null);
meta.put(MSG_KEY_ATTACHMENT_HEADERS, headers); meta.put(MSG_KEY_ATTACHMENT_HEADERS, headers);
if (timer != NO_AUTO_DELETE_TIMER) {
meta.put(MSG_KEY_AUTO_DELETE_TIMER, timer);
}
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }

View File

@@ -17,23 +17,21 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
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;
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;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import java.util.Map; 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.privategroup.PrivateGroupManager.CLIENT_ID;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER; import static org.briarproject.briar.api.privategroup.PrivateGroupManager.MAJOR_VERSION;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID;
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;
import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN; import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN;
@@ -41,7 +39,7 @@ import static org.briarproject.briar.privategroup.invitation.MessageType.LEAVE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
abstract class AbstractProtocolEngine<S extends Session<?>> abstract class AbstractProtocolEngine<S extends Session>
implements ProtocolEngine<S> { implements ProtocolEngine<S> {
protected final DatabaseComponent db; protected final DatabaseComponent db;
@@ -55,23 +53,15 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final MessageParser messageParser; private final MessageParser messageParser;
private final MessageEncoder messageEncoder; private final MessageEncoder messageEncoder;
private final AutoDeleteManager autoDeleteManager;
private final ConversationManager conversationManager;
private final Clock clock; private final Clock clock;
AbstractProtocolEngine( AbstractProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
DatabaseComponent db,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, IdentityManager identityManager, MessageParser messageParser,
MessageParser messageParser, MessageEncoder messageEncoder, MessageTracker messageTracker,
MessageEncoder messageEncoder,
MessageTracker messageTracker,
AutoDeleteManager autoDeleteManager,
ConversationManager conversationManager,
Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
@@ -83,11 +73,16 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
this.messageParser = messageParser; this.messageParser = messageParser;
this.messageEncoder = messageEncoder; this.messageEncoder = messageEncoder;
this.messageTracker = messageTracker; this.messageTracker = messageTracker;
this.autoDeleteManager = autoDeleteManager;
this.conversationManager = conversationManager;
this.clock = clock; this.clock = clock;
} }
ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException, FormatException {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn,
contactGroupId);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
}
boolean isSubscribedPrivateGroup(Transaction txn, GroupId g) boolean isSubscribedPrivateGroup(Transaction txn, GroupId g)
throws DbException { throws DbException {
if (!db.containsGroup(txn, g)) return false; if (!db.containsGroup(txn, g)) return false;
@@ -95,7 +90,6 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
return group.getClientId().equals(PrivateGroupManager.CLIENT_ID); return group.getClientId().equals(PrivateGroupManager.CLIENT_ID);
} }
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
boolean isValidDependency(S session, @Nullable MessageId dependency) { boolean isValidDependency(S session, @Nullable MessageId dependency) {
MessageId expected = session.getLastRemoteMessageId(); MessageId expected = session.getLastRemoteMessageId();
if (dependency == null) return expected == null; if (dependency == null) return expected == null;
@@ -105,108 +99,54 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
void setPrivateGroupVisibility(Transaction txn, S session, void setPrivateGroupVisibility(Transaction txn, S session,
Visibility preferred) throws DbException, FormatException { Visibility preferred) throws DbException, FormatException {
// Apply min of preferred visibility and client's visibility // Apply min of preferred visibility and client's visibility
ContactId contactId = ContactId contactId = getContactId(txn, session.getContactGroupId());
clientHelper.getContactId(txn, session.getContactGroupId());
Visibility client = clientVersioningManager.getClientVisibility(txn, Visibility client = clientVersioningManager.getClientVisibility(txn,
contactId, PrivateGroupManager.CLIENT_ID, contactId, CLIENT_ID, MAJOR_VERSION);
PrivateGroupManager.MAJOR_VERSION);
Visibility min = Visibility.min(preferred, client); Visibility min = Visibility.min(preferred, client);
db.setGroupVisibility(txn, contactId, session.getPrivateGroupId(), min); db.setGroupVisibility(txn, contactId, session.getPrivateGroupId(), min);
} }
Message sendInviteMessage(Transaction txn, S s, Message sendInviteMessage(Transaction txn, S session,
@Nullable String text, long timestamp, byte[] signature, @Nullable String text, long timestamp, byte[] signature)
long timer) throws DbException { throws DbException {
Group g = db.getGroup(txn, s.getPrivateGroupId()); Group g = db.getGroup(txn, session.getPrivateGroupId());
PrivateGroup privateGroup; PrivateGroup privateGroup;
try { try {
privateGroup = privateGroupFactory.parsePrivateGroup(g); privateGroup = privateGroupFactory.parsePrivateGroup(g);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); // Invalid group descriptor throw new DbException(e); // Invalid group descriptor
} }
Message m; Message m = messageEncoder.encodeInviteMessage(
ContactId c = getContactId(txn, s.getContactGroupId()); session.getContactGroupId(), privateGroup.getId(),
if (contactSupportsAutoDeletion(txn, c)) { timestamp, privateGroup.getName(), privateGroup.getCreator(),
m = messageEncoder.encodeInviteMessage(s.getContactGroupId(), privateGroup.getSalt(), text, signature);
privateGroup.getId(), timestamp, privateGroup.getName(), sendMessage(txn, m, INVITE, privateGroup.getId(), true);
privateGroup.getCreator(), privateGroup.getSalt(), text,
signature, timer);
sendMessage(txn, m, INVITE, privateGroup.getId(), true, timer);
} else {
m = messageEncoder.encodeInviteMessage(s.getContactGroupId(),
privateGroup.getId(), timestamp, privateGroup.getName(),
privateGroup.getCreator(), privateGroup.getSalt(), text,
signature);
sendMessage(txn, m, INVITE, privateGroup.getId(), true,
NO_AUTO_DELETE_TIMER);
}
return m; return m;
} }
Message sendJoinMessage(Transaction txn, S s, boolean visibleInUi) Message sendJoinMessage(Transaction txn, S session, boolean visibleInUi)
throws DbException { throws DbException {
Message m; Message m = messageEncoder.encodeJoinMessage(
long localTimestamp = visibleInUi session.getContactGroupId(), session.getPrivateGroupId(),
? getTimestampForVisibleMessage(txn, s) getLocalTimestamp(session), session.getLastLocalMessageId());
: getTimestampForInvisibleMessage(s); sendMessage(txn, m, JOIN, session.getPrivateGroupId(), visibleInUi);
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
// Set auto-delete timer if manually accepting an invitation
long timer = NO_AUTO_DELETE_TIMER;
if (visibleInUi) {
timer = autoDeleteManager
.getAutoDeleteTimer(txn, c, localTimestamp);
}
m = messageEncoder.encodeJoinMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer);
sendMessage(txn, m, JOIN, s.getPrivateGroupId(), visibleInUi,
timer);
} else {
m = messageEncoder.encodeJoinMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId());
sendMessage(txn, m, JOIN, s.getPrivateGroupId(), visibleInUi,
NO_AUTO_DELETE_TIMER);
}
return m; return m;
} }
Message sendLeaveMessage(Transaction txn, S s, boolean visibleInUi) Message sendLeaveMessage(Transaction txn, S session, boolean visibleInUi)
throws DbException { throws DbException {
Message m; Message m = messageEncoder.encodeLeaveMessage(
long localTimestamp = visibleInUi session.getContactGroupId(), session.getPrivateGroupId(),
? getTimestampForVisibleMessage(txn, s) getLocalTimestamp(session), session.getLastLocalMessageId());
: getTimestampForInvisibleMessage(s); sendMessage(txn, m, LEAVE, session.getPrivateGroupId(), visibleInUi);
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
// Set auto-delete timer if manually accepting an invitation
long timer = NO_AUTO_DELETE_TIMER;
if (visibleInUi) {
timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
}
m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer);
sendMessage(txn, m, LEAVE, s.getPrivateGroupId(), visibleInUi,
timer);
} else {
m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId());
sendMessage(txn, m, LEAVE, s.getPrivateGroupId(), visibleInUi,
NO_AUTO_DELETE_TIMER);
}
return m; return m;
} }
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(),
getTimestampForInvisibleMessage(session)); getLocalTimestamp(session));
sendMessage(txn, m, ABORT, session.getPrivateGroupId(), false, sendMessage(txn, m, ABORT, session.getPrivateGroupId(), false);
NO_AUTO_DELETE_TIMER);
return m; return m;
} }
@@ -260,7 +200,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 =
max(clock.currentTimeMillis(), invite.getTimestamp() + 1); Math.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(
@@ -270,49 +210,18 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
.addPrivateGroup(txn, privateGroup, joinMessage, false); .addPrivateGroup(txn, privateGroup, joinMessage, false);
} }
/** long getLocalTimestamp(S session) {
* Returns a timestamp for a visible outgoing message. The timestamp is return Math.max(clock.currentTimeMillis(),
* later than the timestamp of any message sent or received so far in the Math.max(session.getLocalTimestamp(),
* conversation, and later than the {@link #getSessionTimestamp(Session) session.getInviteTimestamp()) + 1);
* 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());
}
void receiveAutoDeleteTimer(Transaction txn,
DeletableGroupInvitationMessage m) throws DbException {
ContactId c = getContactId(txn, m.getContactGroupId());
autoDeleteManager.receiveAutoDeleteTimer(txn, c, m.getAutoDeleteTimer(),
m.getTimestamp());
} }
private void sendMessage(Transaction txn, Message m, MessageType type, private void sendMessage(Transaction txn, Message m, MessageType type,
GroupId privateGroupId, boolean visibleInConversation, GroupId privateGroupId, boolean visibleInConversation)
long autoDeleteTimer) throws DbException { throws DbException {
BdfDictionary meta = messageEncoder.encodeMetadata(type, BdfDictionary meta = messageEncoder
privateGroupId, m.getTimestamp(), true, true, .encodeMetadata(type, privateGroupId, m.getTimestamp(), true,
visibleInConversation, false, false, autoDeleteTimer); true, visibleInConversation, false, false);
try { try {
clientHelper.addLocalMessage(txn, m, meta, true, false); clientHelper.addLocalMessage(txn, m, meta, true, false);
} catch (FormatException e) { } catch (FormatException e) {
@@ -320,21 +229,4 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
} }
} }
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException {
try {
return clientHelper.getContactId(txn, contactGroupId);
} catch (FormatException e) {
throw new DbException(e);
}
}
private boolean contactSupportsAutoDeletion(Transaction txn, ContactId c)
throws DbException {
int minorVersion = clientVersioningManager.getClientMinorVersion(txn, c,
GroupInvitationManager.CLIENT_ID,
GroupInvitationManager.MAJOR_VERSION);
// Auto-delete was added in client version 0.1
return minorVersion >= 1;
}
} }

View File

@@ -11,11 +11,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
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;
@@ -25,7 +23,6 @@ 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;
@@ -39,34 +36,26 @@ import static org.briarproject.briar.privategroup.invitation.CreatorState.START;
@NotNullByDefault @NotNullByDefault
class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> { class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
CreatorProtocolEngine( CreatorProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
DatabaseComponent db,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, IdentityManager identityManager, MessageParser messageParser,
MessageParser messageParser, MessageEncoder messageEncoder, MessageTracker messageTracker,
MessageEncoder messageEncoder,
MessageTracker messageTracker,
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, 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)
long autoDeleteTimer) throws DbException { 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:
@@ -156,16 +145,14 @@ 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)
long autoDeleteTimer) throws DbException { 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 = long localTimestamp = Math.max(timestamp, getLocalTimestamp(s));
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);
@@ -201,13 +188,10 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
// Track the message // Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(), messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false); m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Share the private group with the contact // Share the private group with the contact
setPrivateGroupVisibility(txn, s, SHARED); setPrivateGroupVisibility(txn, s, SHARED);
// Broadcast an event // Broadcast an event
ContactId contactId = ContactId contactId = getContactId(txn, m.getContactGroupId());
clientHelper.getContactId(txn, m.getContactGroupId());
txn.attach(new GroupInvitationResponseReceivedEvent( txn.attach(new GroupInvitationResponseReceivedEvent(
createInvitationResponse(m, true), contactId)); createInvitationResponse(m, true), contactId));
// Move to the JOINED state // Move to the JOINED state
@@ -228,11 +212,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
// Track the message // Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(), messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false); m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Broadcast an event // Broadcast an event
ContactId contactId = ContactId contactId = getContactId(txn, m.getContactGroupId());
clientHelper.getContactId(txn, m.getContactGroupId());
txn.attach(new GroupInvitationResponseReceivedEvent( txn.attach(new GroupInvitationResponseReceivedEvent(
createInvitationResponse(m, false), contactId)); createInvitationResponse(m, false), contactId));
// Move to the START state // Move to the START state
@@ -272,10 +253,10 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
} }
private GroupInvitationResponse createInvitationResponse( private GroupInvitationResponse createInvitationResponse(
DeletableGroupInvitationMessage m, boolean accept) { GroupInvitationMessage m, boolean accept) {
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes()); SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
return new GroupInvitationResponse(m.getId(), m.getContactGroupId(), return new GroupInvitationResponse(m.getId(), m.getContactGroupId(),
m.getTimestamp(), false, false, false, false, sessionId, m.getTimestamp(), false, false, false, false, sessionId,
accept, m.getPrivateGroupId(), m.getAutoDeleteTimer()); accept, m.getPrivateGroupId());
} }
} }

View File

@@ -1,24 +0,0 @@
package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
abstract class DeletableGroupInvitationMessage extends GroupInvitationMessage {
private final long autoDeleteTimer;
DeletableGroupInvitationMessage(MessageId id, GroupId contactGroupId,
GroupId privateGroupId, long timestamp, long autoDeleteTimer) {
super(id, contactGroupId, privateGroupId, timestamp);
this.autoDeleteTimer = autoDeleteTimer;
}
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
}

View File

@@ -2,6 +2,9 @@ package org.briarproject.briar.privategroup.invitation;
interface GroupInvitationConstants { interface GroupInvitationConstants {
// Group metadata keys
String GROUP_KEY_CONTACT_ID = "contactId";
// Message metadata keys // Message metadata keys
String MSG_KEY_MESSAGE_TYPE = "messageType"; String MSG_KEY_MESSAGE_TYPE = "messageType";
String MSG_KEY_PRIVATE_GROUP_ID = "privateGroupId"; String MSG_KEY_PRIVATE_GROUP_ID = "privateGroupId";
@@ -10,7 +13,6 @@ interface GroupInvitationConstants {
String MSG_KEY_VISIBLE_IN_UI = "visibleInUi"; String MSG_KEY_VISIBLE_IN_UI = "visibleInUi";
String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer"; String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer";
String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted"; String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted";
String MSG_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer";
// Session keys // Session keys
String SESSION_KEY_IS_SESSION = "isSession"; String SESSION_KEY_IS_SESSION = "isSession";

View File

@@ -53,6 +53,7 @@ import javax.inject.Inject;
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.START; import static org.briarproject.briar.privategroup.invitation.CreatorState.START;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID;
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;
import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN; import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN;
@@ -122,7 +123,13 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
c.getId(), CLIENT_ID, MAJOR_VERSION); c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client); db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group // Attach the contact ID to the group
clientHelper.setContactId(txn, g.getId(), c.getId()); BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
try {
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
// If the contact belongs to any private groups, create a peer session // If the contact belongs to any private groups, create a peer session
for (Group pg : db.getGroups(txn, PrivateGroupManager.CLIENT_ID, for (Group pg : db.getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION)) { PrivateGroupManager.MAJOR_VERSION)) {
@@ -152,7 +159,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
SessionId sessionId = getSessionId(meta.getPrivateGroupId()); SessionId sessionId = getSessionId(meta.getPrivateGroupId());
StoredSession ss = getSession(txn, m.getGroupId(), sessionId); StoredSession ss = getSession(txn, m.getGroupId(), sessionId);
// Handle the message // Handle the message
Session<?> session; Session session;
MessageId storageId; MessageId storageId;
if (ss == null) { if (ss == null) {
session = handleFirstMessage(txn, m, body, meta); session = handleFirstMessage(txn, m, body, meta);
@@ -182,9 +189,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
results.values().iterator().next()); results.values().iterator().next());
} }
private Session<?> handleFirstMessage(Transaction txn, Message m, private Session handleFirstMessage(Transaction txn, Message m, BdfList body,
BdfList body, MessageMetadata meta) MessageMetadata meta) throws DbException, FormatException {
throws DbException, FormatException {
GroupId privateGroupId = meta.getPrivateGroupId(); GroupId privateGroupId = meta.getPrivateGroupId();
MessageType type = meta.getMessageType(); MessageType type = meta.getMessageType();
if (type == INVITE) { if (type == INVITE) {
@@ -200,7 +206,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
} }
private Session<?> handleMessage(Transaction txn, Message m, BdfList body, private Session handleMessage(Transaction txn, Message m, BdfList body,
MessageMetadata meta, BdfDictionary bdfSession) MessageMetadata meta, BdfDictionary bdfSession)
throws DbException, FormatException { throws DbException, FormatException {
MessageType type = meta.getMessageType(); MessageType type = meta.getMessageType();
@@ -222,7 +228,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
} }
private <S extends Session<?>> S handleMessage(Transaction txn, Message m, private <S extends Session> S handleMessage(Transaction txn, Message m,
BdfList body, MessageType type, S session, ProtocolEngine<S> engine) BdfList body, MessageType type, S session, ProtocolEngine<S> engine)
throws DbException, FormatException { throws DbException, FormatException {
if (type == INVITE) { if (type == INVITE) {
@@ -250,15 +256,15 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
private void storeSession(Transaction txn, MessageId storageId, private void storeSession(Transaction txn, MessageId storageId,
Session<?> session) throws DbException, FormatException { Session session) throws DbException, FormatException {
BdfDictionary d = sessionEncoder.encodeSession(session); BdfDictionary d = sessionEncoder.encodeSession(session);
clientHelper.mergeMessageMetadata(txn, storageId, d); clientHelper.mergeMessageMetadata(txn, storageId, d);
} }
@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)
long autoDeleteTimer) throws DbException { throws DbException {
SessionId sessionId = getSessionId(privateGroupId); SessionId sessionId = getSessionId(privateGroupId);
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
@@ -281,7 +287,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, autoDeleteTimer); timestamp, signature);
// Store the updated session // Store the updated session
storeSession(txn, storageId, session); storeSession(txn, storageId, session);
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -348,7 +354,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
} }
private <S extends Session<?>> S handleAction(Transaction txn, private <S extends Session> S handleAction(Transaction txn,
LocalAction type, S session, ProtocolEngine<S> engine) LocalAction type, S session, ProtocolEngine<S> engine)
throws DbException { throws DbException {
if (type == LocalAction.INVITE) { if (type == LocalAction.INVITE) {
@@ -414,8 +420,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
return new GroupInvitationRequest(m, contactGroupId, return new GroupInvitationRequest(m, contactGroupId,
meta.getTimestamp(), meta.isLocal(), meta.isRead(), meta.getTimestamp(), meta.isLocal(), meta.isRead(),
status.isSent(), status.isSeen(), sessionId, pg, status.isSent(), status.isSeen(), sessionId, pg,
invite.getText(), meta.isAvailableToAnswer(), canBeOpened, invite.getText(), meta.isAvailableToAnswer(), canBeOpened);
invite.getAutoDeleteTimer());
} }
private GroupInvitationResponse parseInvitationResponse( private GroupInvitationResponse parseInvitationResponse(
@@ -425,7 +430,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
return new GroupInvitationResponse(m, contactGroupId, return new GroupInvitationResponse(m, contactGroupId,
meta.getTimestamp(), meta.isLocal(), meta.isRead(), meta.getTimestamp(), meta.isLocal(), meta.isRead(),
status.isSent(), status.isSeen(), sessionId, accept, status.isSent(), status.isSeen(), sessionId, accept,
meta.getPrivateGroupId(), meta.getAutoDeleteTimer()); meta.getPrivateGroupId());
} }
@Override @Override
@@ -503,7 +508,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
SessionId sessionId = getSessionId(privateGroupId); SessionId sessionId = getSessionId(privateGroupId);
StoredSession ss = getSession(txn, contactGroupId, sessionId); StoredSession ss = getSession(txn, contactGroupId, sessionId);
// Create or parse the session // Create or parse the session
Session<?> session; Session session;
MessageId storageId; MessageId storageId;
if (ss == null) { if (ss == null) {
// If there's no session the contact must be a peer, // If there's no session the contact must be a peer,
@@ -538,7 +543,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
StoredSession ss = getSession(txn, contactGroupId, sessionId); StoredSession ss = getSession(txn, contactGroupId, sessionId);
if (ss == null) continue; // No session for this contact if (ss == null) continue; // No session for this contact
// Handle the action // Handle the action
Session<?> session = handleAction(txn, LocalAction.LEAVE, Session session = handleAction(txn, LocalAction.LEAVE,
contactGroupId, ss.bdfSession); contactGroupId, ss.bdfSession);
// Store the updated session // Store the updated session
storeSession(txn, ss.storageId, session); storeSession(txn, ss.storageId, session);
@@ -548,7 +553,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
} }
private Session<?> handleAction(Transaction txn, LocalAction a, private Session handleAction(Transaction txn, LocalAction a,
GroupId contactGroupId, BdfDictionary bdfSession) GroupId contactGroupId, BdfDictionary bdfSession)
throws DbException, FormatException { throws DbException, FormatException {
Role role = sessionParser.getRole(bdfSession); Role role = sessionParser.getRole(bdfSession);
@@ -608,7 +613,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
.getMessageMetadataAsDictionary(txn, contactGroupId, query); .getMessageMetadataAsDictionary(txn, contactGroupId, query);
Map<GroupId, Visibility> m = new HashMap<>(); Map<GroupId, Visibility> m = new HashMap<>();
for (BdfDictionary d : results.values()) { for (BdfDictionary d : results.values()) {
Session<?> s = sessionParser.parseSession(contactGroupId, d); Session s = sessionParser.parseSession(contactGroupId, d);
m.put(s.getPrivateGroupId(), s.getState().getVisibility()); m.put(s.getPrivateGroupId(), s.getState().getVisibility());
} }
return m; return m;
@@ -639,7 +644,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
Map<GroupId, DeletableSession> sessions = new HashMap<>(); Map<GroupId, DeletableSession> sessions = new HashMap<>();
for (BdfDictionary d : metadata.values()) { for (BdfDictionary d : metadata.values()) {
if (!sessionParser.isSession(d)) continue; if (!sessionParser.isSession(d)) continue;
Session<?> session; Session session;
try { try {
session = sessionParser.parseSession(g, d); session = sessionParser.parseSession(g, d);
} catch (FormatException e) { } catch (FormatException e) {
@@ -668,7 +673,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
getSessionId(messageMetadata.getPrivateGroupId()); getSessionId(messageMetadata.getPrivateGroupId());
StoredSession ss = getSession(txn1, g, sessionId); StoredSession ss = getSession(txn1, g, sessionId);
if (ss == null) throw new DbException(); if (ss == null) throw new DbException();
Session<?> session = sessionParser Session session = sessionParser
.parseSession(g, metadata.get(ss.storageId)); .parseSession(g, metadata.get(ss.storageId));
sessions.put(session.getPrivateGroupId(), sessions.put(session.getPrivateGroupId(),
new DeletableSession(session.getState())); new DeletableSession(session.getState()));

View File

@@ -26,7 +26,6 @@ import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH; import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_TEXT_LENGTH; import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_TEXT_LENGTH;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH; import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
@@ -35,7 +34,6 @@ 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;
import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN; import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN;
import static org.briarproject.briar.privategroup.invitation.MessageType.LEAVE; import static org.briarproject.briar.privategroup.invitation.MessageType.LEAVE;
import static org.briarproject.briar.util.ValidationUtils.validateAutoDeleteTimer;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -73,11 +71,8 @@ class GroupInvitationValidator extends BdfMessageValidator {
private BdfMessageContext validateInviteMessage(Message m, BdfList body) private BdfMessageContext validateInviteMessage(Message m, BdfList body)
throws FormatException { throws FormatException {
// Client version 0.0: Message type, creator, group name, salt, // Message type, creator, group name, salt, optional text, signature
// optional text, signature. checkSize(body, 6);
// Client version 0.1: Message type, creator, group name, salt,
// optional text, signature, optional auto-delete timer.
checkSize(body, 6, 7);
BdfList creatorList = body.getList(1); BdfList creatorList = body.getList(1);
String groupName = body.getString(2); String groupName = body.getString(2);
checkLength(groupName, 1, MAX_GROUP_NAME_LENGTH); checkLength(groupName, 1, MAX_GROUP_NAME_LENGTH);
@@ -87,10 +82,6 @@ class GroupInvitationValidator extends BdfMessageValidator {
checkLength(text, 1, MAX_GROUP_INVITATION_TEXT_LENGTH); checkLength(text, 1, MAX_GROUP_INVITATION_TEXT_LENGTH);
byte[] signature = body.getRaw(5); byte[] signature = body.getRaw(5);
checkLength(signature, 1, MAX_SIGNATURE_LENGTH); checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 7) {
timer = validateAutoDeleteTimer(body.getOptionalLong(6));
}
// Validate the creator and create the private group // Validate the creator and create the private group
Author creator = clientHelper.parseAndValidateAuthor(creatorList); Author creator = clientHelper.parseAndValidateAuthor(creatorList);
@@ -111,29 +102,20 @@ class GroupInvitationValidator extends BdfMessageValidator {
// Create the metadata // Create the metadata
BdfDictionary meta = messageEncoder.encodeMetadata(INVITE, BdfDictionary meta = messageEncoder.encodeMetadata(INVITE,
privateGroup.getId(), m.getTimestamp(), false, false, false, privateGroup.getId(), m.getTimestamp(), false, false, false,
false, false, timer); false, false);
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }
private BdfMessageContext validateJoinMessage(Message m, BdfList body) private BdfMessageContext validateJoinMessage(Message m, BdfList body)
throws FormatException { throws FormatException {
// Client version 0.0: Message type, private group ID, optional checkSize(body, 3);
// previous message ID.
// Client version 0.1: Message type, private group ID, optional
// previous message ID, optional auto-delete timer.
checkSize(body, 3, 4);
byte[] privateGroupId = body.getRaw(1); byte[] privateGroupId = body.getRaw(1);
checkLength(privateGroupId, UniqueId.LENGTH); checkLength(privateGroupId, UniqueId.LENGTH);
byte[] previousMessageId = body.getOptionalRaw(2); byte[] previousMessageId = body.getOptionalRaw(2);
checkLength(previousMessageId, UniqueId.LENGTH); checkLength(previousMessageId, UniqueId.LENGTH);
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 4) {
timer = validateAutoDeleteTimer(body.getOptionalLong(3));
}
BdfDictionary meta = messageEncoder.encodeMetadata(JOIN, BdfDictionary meta = messageEncoder.encodeMetadata(JOIN,
new GroupId(privateGroupId), m.getTimestamp(), false, false, new GroupId(privateGroupId), m.getTimestamp(), false, false,
false, false, false, timer); false, false, false);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -145,23 +127,14 @@ class GroupInvitationValidator extends BdfMessageValidator {
private BdfMessageContext validateLeaveMessage(Message m, BdfList body) private BdfMessageContext validateLeaveMessage(Message m, BdfList body)
throws FormatException { throws FormatException {
// Client version 0.0: Message type, private group ID, optional checkSize(body, 3);
// previous message ID.
// Client version 0.1: Message type, private group ID, optional
// previous message ID, optional auto-delete timer.
checkSize(body, 3, 4);
byte[] privateGroupId = body.getRaw(1); byte[] privateGroupId = body.getRaw(1);
checkLength(privateGroupId, UniqueId.LENGTH); checkLength(privateGroupId, UniqueId.LENGTH);
byte[] previousMessageId = body.getOptionalRaw(2); byte[] previousMessageId = body.getOptionalRaw(2);
checkLength(previousMessageId, UniqueId.LENGTH); checkLength(previousMessageId, UniqueId.LENGTH);
long timer = NO_AUTO_DELETE_TIMER;
if (body.size() == 4) {
timer = validateAutoDeleteTimer(body.getOptionalLong(3));
}
BdfDictionary meta = messageEncoder.encodeMetadata(LEAVE, BdfDictionary meta = messageEncoder.encodeMetadata(LEAVE,
new GroupId(privateGroupId), m.getTimestamp(), false, false, new GroupId(privateGroupId), m.getTimestamp(), false, false,
false, false, false, timer); false, false, false);
if (previousMessageId == null) { if (previousMessageId == null) {
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} else { } else {
@@ -178,7 +151,7 @@ class GroupInvitationValidator extends BdfMessageValidator {
checkLength(privateGroupId, UniqueId.LENGTH); checkLength(privateGroupId, UniqueId.LENGTH);
BdfDictionary meta = messageEncoder.encodeMetadata(ABORT, BdfDictionary meta = messageEncoder.encodeMetadata(ABORT,
new GroupId(privateGroupId), m.getTimestamp(), false, false, new GroupId(privateGroupId), m.getTimestamp(), false, false,
false, false, false, NO_AUTO_DELETE_TIMER); false, false, false);
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }
} }

View File

@@ -10,7 +10,7 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class InviteMessage extends DeletableGroupInvitationMessage { class InviteMessage extends GroupInvitationMessage {
private final String groupName; private final String groupName;
private final Author creator; private final Author creator;
@@ -20,8 +20,8 @@ class InviteMessage extends DeletableGroupInvitationMessage {
InviteMessage(MessageId id, GroupId contactGroupId, GroupId privateGroupId, InviteMessage(MessageId id, GroupId contactGroupId, GroupId privateGroupId,
long timestamp, String groupName, Author creator, byte[] salt, long timestamp, String groupName, Author creator, byte[] salt,
@Nullable String text, byte[] signature, long autoDeleteTimer) { @Nullable String text, byte[] signature) {
super(id, contactGroupId, privateGroupId, timestamp, autoDeleteTimer); super(id, contactGroupId, privateGroupId, timestamp);
this.groupName = groupName; this.groupName = groupName;
this.creator = creator; this.creator = creator;
this.salt = salt; this.salt = salt;

View File

@@ -13,11 +13,9 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
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;
@@ -43,30 +41,22 @@ import static org.briarproject.briar.privategroup.invitation.InviteeState.START;
@NotNullByDefault @NotNullByDefault
class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> { class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
InviteeProtocolEngine( InviteeProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
DatabaseComponent db,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, IdentityManager identityManager, MessageParser messageParser,
MessageParser messageParser, MessageEncoder messageEncoder, MessageTracker messageTracker,
MessageEncoder messageEncoder,
MessageTracker messageTracker,
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, 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
} }
@@ -240,8 +230,7 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
// The timestamp must be higher than the last invite message, if any // The timestamp must be higher than the last invite message, if any
if (m.getTimestamp() <= s.getInviteTimestamp()) return abort(txn, s); if (m.getTimestamp() <= s.getInviteTimestamp()) return abort(txn, s);
// Check that the contact is the creator // Check that the contact is the creator
ContactId contactId = ContactId contactId = getContactId(txn, s.getContactGroupId());
clientHelper.getContactId(txn, s.getContactGroupId());
Author contact = db.getContact(txn, contactId).getAuthor(); Author contact = db.getContact(txn, contactId).getAuthor();
if (!contact.getId().equals(m.getCreator().getId())) if (!contact.getId().equals(m.getCreator().getId()))
return abort(txn, s); return abort(txn, s);
@@ -251,8 +240,6 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
// Track the message // Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(), messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false); m.getTimestamp(), false);
// Receive the auto-delete timer
receiveAutoDeleteTimer(txn, m);
// Broadcast an event // Broadcast an event
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup( PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
m.getGroupName(), m.getCreator(), m.getSalt()); m.getGroupName(), m.getCreator(), m.getSalt());
@@ -343,7 +330,7 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes()); SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
return new GroupInvitationRequest(m.getId(), m.getContactGroupId(), return new GroupInvitationRequest(m.getId(), m.getContactGroupId(),
m.getTimestamp(), false, false, false, false, sessionId, pg, m.getTimestamp(), false, false, false, false, sessionId, pg,
m.getText(), true, false, m.getAutoDeleteTimer()); m.getText(), true, false);
} }
} }

View File

@@ -9,15 +9,14 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class JoinMessage extends DeletableGroupInvitationMessage { class JoinMessage extends GroupInvitationMessage {
@Nullable @Nullable
private final MessageId previousMessageId; private final MessageId previousMessageId;
JoinMessage(MessageId id, GroupId contactGroupId, GroupId privateGroupId, JoinMessage(MessageId id, GroupId contactGroupId, GroupId privateGroupId,
long timestamp, @Nullable MessageId previousMessageId, long timestamp, @Nullable MessageId previousMessageId) {
long autoDeleteTimer) { super(id, contactGroupId, privateGroupId, timestamp);
super(id, contactGroupId, privateGroupId, timestamp, autoDeleteTimer);
this.previousMessageId = previousMessageId; this.previousMessageId = previousMessageId;
} }

View File

@@ -9,15 +9,14 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class LeaveMessage extends DeletableGroupInvitationMessage { class LeaveMessage extends GroupInvitationMessage {
@Nullable @Nullable
private final MessageId previousMessageId; private final MessageId previousMessageId;
LeaveMessage(MessageId id, GroupId contactGroupId, GroupId privateGroupId, LeaveMessage(MessageId id, GroupId contactGroupId, GroupId privateGroupId,
long timestamp, @Nullable MessageId previousMessageId, long timestamp, @Nullable MessageId previousMessageId) {
long autoDeleteTimer) { super(id, contactGroupId, privateGroupId, timestamp);
super(id, contactGroupId, privateGroupId, timestamp, autoDeleteTimer);
this.previousMessageId = previousMessageId; this.previousMessageId = previousMessageId;
} }

View File

@@ -14,7 +14,7 @@ interface MessageEncoder {
BdfDictionary encodeMetadata(MessageType type, GroupId privateGroupId, BdfDictionary encodeMetadata(MessageType type, GroupId privateGroupId,
long timestamp, boolean local, boolean read, boolean visible, long timestamp, boolean local, boolean read, boolean visible,
boolean available, boolean accepted, long autoDeleteTimer); boolean available, boolean accepted);
void setVisibleInUi(BdfDictionary meta, boolean visible); void setVisibleInUi(BdfDictionary meta, boolean visible);
@@ -22,49 +22,16 @@ interface MessageEncoder {
void setInvitationAccepted(BdfDictionary meta, boolean accepted); void setInvitationAccepted(BdfDictionary meta, boolean accepted);
/**
* Encodes an invite message without an auto-delete timer.
*/
Message encodeInviteMessage(GroupId contactGroupId, GroupId privateGroupId, Message encodeInviteMessage(GroupId contactGroupId, GroupId privateGroupId,
long timestamp, String groupName, Author creator, byte[] salt, long timestamp, String groupName, Author creator, byte[] salt,
@Nullable String text, byte[] signature); @Nullable String text, byte[] signature);
/**
* Encodes an invite message with an optional auto-delete timer. This
* requires the contact to support client version 0.1 or higher.
*/
Message encodeInviteMessage(GroupId contactGroupId, GroupId privateGroupId,
long timestamp, String groupName, Author creator, byte[] salt,
@Nullable String text, byte[] signature, long autoDeleteTimer);
/**
* Encodes a join message without an auto-delete timer.
*/
Message encodeJoinMessage(GroupId contactGroupId, GroupId privateGroupId, Message encodeJoinMessage(GroupId contactGroupId, GroupId privateGroupId,
long timestamp, @Nullable MessageId previousMessageId); long timestamp, @Nullable MessageId previousMessageId);
/**
* Encodes a join message with an optional auto-delete timer. This
* requires the contact to support client version 0.1 or higher.
*/
Message encodeJoinMessage(GroupId contactGroupId, GroupId privateGroupId,
long timestamp, @Nullable MessageId previousMessageId,
long autoDeleteTimer);
/**
* Encodes a leave message without an auto-delete timer.
*/
Message encodeLeaveMessage(GroupId contactGroupId, GroupId privateGroupId, Message encodeLeaveMessage(GroupId contactGroupId, GroupId privateGroupId,
long timestamp, @Nullable MessageId previousMessageId); long timestamp, @Nullable MessageId previousMessageId);
/**
* Encodes a leave message with an optional auto-delete timer. This
* requires the contact to support client version 0.1 or higher.
*/
Message encodeLeaveMessage(GroupId contactGroupId, GroupId privateGroupId,
long timestamp, @Nullable MessageId previousMessageId,
long autoDeleteTimer);
Message encodeAbortMessage(GroupId contactGroupId, GroupId privateGroupId, Message encodeAbortMessage(GroupId contactGroupId, GroupId privateGroupId,
long timestamp); long timestamp);
} }

Some files were not shown because too many files have changed in this diff Show More