diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/autodelete/AutoDeleteConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/autodelete/AutoDeleteConstants.java new file mode 100644 index 000000000..73db007ba --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/autodelete/AutoDeleteConstants.java @@ -0,0 +1,11 @@ +package org.briarproject.bramble.api.autodelete; + +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.MINUTES; + +public interface AutoDeleteConstants { + + long MIN_AUTO_DELETE_TIMER_MS = MINUTES.toMillis(1); + + long MAX_AUTO_DELETE_TIMER_MS = DAYS.toMillis(365); +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java index 01e0c1647..65b9f23d0 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java @@ -138,6 +138,7 @@ import static org.briarproject.briar.android.util.UiUtils.observeOnce; import static org.briarproject.briar.android.view.AuthorView.setAvatar; 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.PrivateMessageFormat.TEXT; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -268,15 +269,11 @@ public class ConversationActivity extends BriarActivity ImagePreview imagePreview = findViewById(R.id.imagePreview); sendController = new TextAttachmentController(textInputView, imagePreview, this, viewModel); - viewModel.hasImageSupport().observe(this, new Observer() { - @Override - public void onChanged(@Nullable Boolean hasSupport) { - if (hasSupport != null && hasSupport) { - // TODO: remove cast when removing feature flag - ((TextAttachmentController) sendController) - .setImagesSupported(); - viewModel.hasImageSupport().removeObserver(this); - } + observeOnce(viewModel.getPrivateMessageFormat(), this, format -> { + if (format != null && format != TEXT) { + // TODO: remove cast when removing feature flag + ((TextAttachmentController) sendController) + .setImagesSupported(); } }); } else { @@ -640,8 +637,8 @@ public class ConversationActivity extends BriarActivity supportFinishAfterTransition(); } } else if (e instanceof ConversationMessageReceivedEvent) { - ConversationMessageReceivedEvent p = - (ConversationMessageReceivedEvent) e; + ConversationMessageReceivedEvent p = + (ConversationMessageReceivedEvent) e; if (p.getContactId().equals(contactId)) { LOG.info("Message received, adding"); onNewConversationMessage(p.getMessageHeader()); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java index 2a7e23dc7..e499de6af 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java @@ -38,6 +38,7 @@ import org.briarproject.briar.api.identity.AuthorManager; import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.PrivateMessage; 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.event.AttachmentReceivedEvent; @@ -57,11 +58,14 @@ import static androidx.lifecycle.Transformations.map; import static java.util.Objects.requireNonNull; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS; import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce; +import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT; +import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES; @NotNullByDefault public class ConversationViewModel extends DbViewModel @@ -92,7 +96,7 @@ public class ConversationViewModel extends DbViewModel private final LiveData contactName = map(contactItem, c -> UiUtils.getContactDisplayName(c.getContact())); private final LiveData messagingGroupId; - private final MutableLiveData imageSupport = + private final MutableLiveData privateMessageFormat = new MutableLiveData<>(); private final MutableLiveEvent showImageOnboarding = new MutableLiveEvent<>(); @@ -241,11 +245,11 @@ public class ConversationViewModel extends DbViewModel // messagingGroupId is loaded with the contact observeForeverOnce(messagingGroupId, groupId -> { requireNonNull(groupId); - observeForeverOnce(imageSupport, hasImageSupport -> { - requireNonNull(hasImageSupport); - createMessage(groupId, text, headers, timestamp, - hasImageSupport); - }); + // TODO: Use the timer duration that was fetched when checking the + // message format + observeForeverOnce(privateMessageFormat, format -> + createMessage(groupId, text, headers, timestamp, + format)); }); } @@ -275,10 +279,10 @@ public class ConversationViewModel extends DbViewModel @DatabaseExecutor private void checkFeaturesAndOnboarding(ContactId c) throws DbException { - // check if images are supported - boolean imagesSupported = db.transactionWithResult(true, txn -> - messagingManager.contactSupportsImages(txn, c)); - imageSupport.postValue(imagesSupported); + // check if images and auto-deletion are supported + PrivateMessageFormat format = db.transactionWithResult(true, txn -> + messagingManager.getContactMessageFormat(txn, c)); + privateMessageFormat.postValue(format); // check if introductions are supported Collection contacts = contactManager.getContacts(); @@ -287,7 +291,7 @@ public class ConversationViewModel extends DbViewModel // we only show one onboarding dialog at a time Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE); - if (imagesSupported && + if (format != TEXT && settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) { onOnboardingShown(SHOW_ONBOARDING_IMAGE); showImageOnboarding.postEvent(true); @@ -308,15 +312,20 @@ public class ConversationViewModel extends DbViewModel @UiThread private void createMessage(GroupId groupId, @Nullable String text, List headers, long timestamp, - boolean hasImageSupport) { + PrivateMessageFormat format) { + // TODO: Move this inside the DB transaction that stores the message + // so we can look up the timer duration (if needed) in the same txn try { PrivateMessage pm; - if (hasImageSupport) { + if (format == TEXT) { + pm = privateMessageFactory.createLegacyPrivateMessage( + groupId, timestamp, requireNonNull(text)); + } else if (format == TEXT_IMAGES) { pm = privateMessageFactory.createPrivateMessage(groupId, timestamp, text, headers); } else { - pm = privateMessageFactory.createLegacyPrivateMessage( - groupId, timestamp, requireNonNull(text)); + pm = privateMessageFactory.createPrivateMessage(groupId, + timestamp, text, headers, MIN_AUTO_DELETE_TIMER_MS); } storeMessage(pm); } catch (FormatException e) { @@ -336,7 +345,8 @@ public class ConversationViewModel extends DbViewModel PrivateMessageHeader h = new PrivateMessageHeader( message.getId(), message.getGroupId(), message.getTimestamp(), true, true, false, false, - m.hasText(), m.getAttachmentHeaders()); + m.hasText(), m.getAttachmentHeaders(), + m.getAutoDeleteTimer()); // TODO add text to cache when available here addedHeader.postEvent(h); } catch (DbException e) { @@ -357,8 +367,8 @@ public class ConversationViewModel extends DbViewModel return contactName; } - LiveData hasImageSupport() { - return imageSupport; + LiveData getPrivateMessageFormat() { + return privateMessageFormat; } LiveEvent showImageOnboarding() { diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java index dfe18806a..1ea995c8e 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java @@ -32,7 +32,7 @@ public interface MessagingManager extends ConversationClient { /** * The current minor version of the messaging client. */ - int MINOR_VERSION = 2; + int MINOR_VERSION = 3; /** * Stores a local private message. @@ -70,12 +70,8 @@ public interface MessagingManager extends ConversationClient { String getMessageText(MessageId m) throws DbException; /** - * Returns true if the contact with the given {@link ContactId} does support - * image attachments. - *

- * Added: 2019-01-01 + * Returns the private message format supported by the given contact. */ - boolean contactSupportsImages(Transaction txn, ContactId c) + PrivateMessageFormat getContactMessageFormat(Transaction txn, ContactId c) throws DbException; - } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java index 188364444..b4bf1b859 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java @@ -9,44 +9,65 @@ import java.util.List; import javax.annotation.concurrent.Immutable; import static java.util.Collections.emptyList; +import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT; +import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES; +import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES_AUTO_DELETE; @Immutable @NotNullByDefault public class PrivateMessage { private final Message message; - private final boolean legacyFormat, hasText; + private final boolean hasText; private final List attachmentHeaders; + private final long autoDeleteTimer; + private final PrivateMessageFormat format; /** - * Constructor for private messages in the legacy format, which does not - * support attachments. + * Constructor for private messages in the + * {@link PrivateMessageFormat#TEXT TEXT} format. */ public PrivateMessage(Message message) { this.message = message; - legacyFormat = true; hasText = true; attachmentHeaders = emptyList(); + autoDeleteTimer = -1; + format = TEXT; } /** - * Constructor for private messages in the current format, which supports - * attachments. + * Constructor for private messages in the + * {@link PrivateMessageFormat#TEXT_IMAGES TEXT_IMAGES} format. */ public PrivateMessage(Message message, boolean hasText, List headers) { this.message = message; this.hasText = hasText; this.attachmentHeaders = headers; - legacyFormat = false; + autoDeleteTimer = -1; + 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 headers, long autoDeleteTimer) { + this.message = message; + this.hasText = hasText; + this.attachmentHeaders = headers; + this.autoDeleteTimer = autoDeleteTimer; + format = TEXT_IMAGES_AUTO_DELETE; } public Message getMessage() { return message; } - public boolean isLegacyFormat() { - return legacyFormat; + public PrivateMessageFormat getFormat() { + return format; } public boolean hasText() { @@ -56,4 +77,8 @@ public class PrivateMessage { public List getAttachmentHeaders() { return attachmentHeaders; } + + public long getAutoDeleteTimer() { + return autoDeleteTimer; + } } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java index da4532015..90c43ccc5 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java @@ -19,4 +19,7 @@ public interface PrivateMessageFactory { @Nullable String text, List headers) throws FormatException; + PrivateMessage createPrivateMessage(GroupId groupId, long timestamp, + @Nullable String text, List headers, + long autoDeleteTimer) throws FormatException; } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFormat.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFormat.java new file mode 100644 index 000000000..6982e0b31 --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFormat.java @@ -0,0 +1,24 @@ +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, + + /** + * 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 +} diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java index 9e17af615..369401982 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java @@ -17,13 +17,16 @@ public class PrivateMessageHeader extends ConversationMessageHeader { private final boolean hasText; private final List attachmentHeaders; + private final long autoDeleteTimer; public PrivateMessageHeader(MessageId id, GroupId groupId, long timestamp, boolean local, boolean read, boolean sent, boolean seen, - boolean hasText, List headers) { + boolean hasText, List headers, + long autoDeleteTimer) { super(id, groupId, timestamp, local, read, sent, seen); this.hasText = hasText; this.attachmentHeaders = headers; + this.autoDeleteTimer = autoDeleteTimer; } public boolean hasText() { @@ -34,9 +37,12 @@ public class PrivateMessageHeader extends ConversationMessageHeader { return attachmentHeaders; } + public long getAutoDeleteTimer() { + return autoDeleteTimer; + } + @Override public T accept(ConversationMessageVisitor v) { return v.visitPrivateMessageHeader(this); } - } diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java index a280ba46a..d62fe2b4a 100644 --- a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java +++ b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java @@ -11,4 +11,5 @@ interface MessagingConstants { String MSG_KEY_MSG_TYPE = "messageType"; String MSG_KEY_HAS_TEXT = "hasText"; String MSG_KEY_ATTACHMENT_HEADERS = "attachmentHeaders"; + String MSG_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer"; } diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java index 0688ee14d..c70ff8196 100644 --- a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java @@ -36,6 +36,7 @@ import org.briarproject.briar.api.conversation.ConversationMessageHeader; import org.briarproject.briar.api.conversation.DeletionResult; import org.briarproject.briar.api.messaging.MessagingManager; 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.event.AttachmentReceivedEvent; import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent; @@ -59,11 +60,15 @@ import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERE import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE; import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH; +import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT; +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.client.MessageTrackerConstants.MSG_KEY_READ; import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT; 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_AUTO_DELETE_TIMER; 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_MSG_TYPE; @@ -193,9 +198,10 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook, long timestamp = meta.getLong(MSG_KEY_TIMESTAMP); boolean local = meta.getBoolean(MSG_KEY_LOCAL); boolean read = meta.getBoolean(MSG_KEY_READ); + long timer = meta.getLong(MSG_KEY_AUTO_DELETE_TIMER, -1L); PrivateMessageHeader header = new PrivateMessageHeader(m.getId(), groupId, timestamp, local, - read, false, false, hasText, headers); + read, false, false, hasText, headers, timer); ContactId contactId = getContactId(txn, groupId); PrivateMessageReceivedEvent event = new PrivateMessageReceivedEvent(header, contactId); @@ -204,8 +210,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook, } private List parseAttachmentHeaders(GroupId g, - BdfDictionary meta) - throws FormatException { + BdfDictionary meta) throws FormatException { BdfList attachmentHeaders = meta.getList(MSG_KEY_ATTACHMENT_HEADERS); int length = attachmentHeaders.size(); List headers = new ArrayList<>(length); @@ -232,7 +237,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook, meta.put(MSG_KEY_TIMESTAMP, m.getMessage().getTimestamp()); meta.put(MSG_KEY_LOCAL, true); meta.put(MSG_KEY_READ, true); - if (!m.isLegacyFormat()) { + if (m.getFormat() != TEXT) { meta.put(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE); meta.put(MSG_KEY_HAS_TEXT, m.hasText()); BdfList headers = new BdfList(); @@ -241,6 +246,10 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook, BdfList.of(a.getMessageId(), a.getContentType())); } meta.put(MSG_KEY_ATTACHMENT_HEADERS, headers); + if (m.getFormat() == TEXT_IMAGES_AUTO_DELETE) { + long timer = m.getAutoDeleteTimer(); + if (timer != -1) meta.put(MSG_KEY_AUTO_DELETE_TIMER, timer); + } } // Mark attachments as shared and permanent now we're ready to send for (AttachmentHeader a : m.getAttachmentHeaders()) { @@ -353,12 +362,13 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook, if (messageType == null) { headers.add(new PrivateMessageHeader(id, g, timestamp, local, read, s.isSent(), s.isSeen(), true, - emptyList())); + emptyList(), -1)); } else { boolean hasText = meta.getBoolean(MSG_KEY_HAS_TEXT); + long timer = meta.getLong(MSG_KEY_AUTO_DELETE_TIMER, -1L); headers.add(new PrivateMessageHeader(id, g, timestamp, local, read, s.isSent(), s.isSeen(), hasText, - parseAttachmentHeaders(g, meta))); + parseAttachmentHeaders(g, meta), timer)); } } catch (FormatException e) { throw new DbException(e); @@ -399,12 +409,13 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook, } @Override - public boolean contactSupportsImages(Transaction txn, ContactId c) - throws DbException { + public PrivateMessageFormat getContactMessageFormat(Transaction txn, + ContactId c) throws DbException { int minorVersion = clientVersioningManager .getClientMinorVersion(txn, c, CLIENT_ID, 0); - // support was added in 0.1 - return minorVersion > 0; + if (minorVersion >= 3) return TEXT_IMAGES_AUTO_DELETE; + else if (minorVersion >= 1) return TEXT_IMAGES; + else return TEXT; } @Override diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java index c4b7f54f7..bf70b109b 100644 --- a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java @@ -47,21 +47,42 @@ class PrivateMessageFactoryImpl implements PrivateMessageFactory { public PrivateMessage createPrivateMessage(GroupId groupId, long timestamp, @Nullable String text, List headers) throws FormatException { - // Validate the arguments + validateTextAndAttachmentHeaders(text, headers); + 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, -1); + } + + @Override + public PrivateMessage createPrivateMessage(GroupId groupId, long timestamp, + @Nullable String text, List headers, + long autoDeleteTimer) throws FormatException { + validateTextAndAttachmentHeaders(text, headers); + BdfList attachmentList = serialiseAttachmentHeaders(headers); + // Serialise the message + Long timer = autoDeleteTimer == -1 ? 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 headers) { if (text == null) { if (headers.isEmpty()) throw new IllegalArgumentException(); } else if (utf8IsTooLong(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH)) { throw new IllegalArgumentException(); } - // Serialise the attachment headers + } + + private BdfList serialiseAttachmentHeaders(List headers) { BdfList attachmentList = new BdfList(); for (AttachmentHeader a : headers) { attachmentList.add( BdfList.of(a.getMessageId(), a.getContentType())); } - // 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); + return attachmentList; } } diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java index af735c73c..5597190b5 100644 --- a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java @@ -24,6 +24,8 @@ import java.io.InputStream; import javax.annotation.concurrent.Immutable; +import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS; +import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.util.ValidationUtils.checkLength; @@ -37,6 +39,7 @@ 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.PRIVATE_MESSAGE; 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_HAS_TEXT; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_MSG_TYPE; @@ -114,8 +117,11 @@ class PrivateMessageValidator implements MessageValidator { private BdfMessageContext validatePrivateMessage(Message m, BdfList body) throws FormatException { - // Message type, optional private message text, attachment headers - checkSize(body, 3); + // Version 0.1: Message type, optional private message text, + // attachment headers. + // Version 0.2: Message type, optional private message text, + // attachment headers, optional auto-delete timer. + checkSize(body, 3, 4); String text = body.getOptionalString(1); checkLength(text, 0, MAX_PRIVATE_MESSAGE_TEXT_LENGTH); BdfList headers = body.getList(2); @@ -130,6 +136,12 @@ class PrivateMessageValidator implements MessageValidator { String contentType = header.getString(1); checkLength(contentType, 1, MAX_CONTENT_TYPE_BYTES); } + Long timer = null; + if (body.size() == 4) timer = body.getOptionalLong(3); + if (timer != null && (timer < MIN_AUTO_DELETE_TIMER_MS || + timer > MAX_AUTO_DELETE_TIMER_MS)) { + throw new FormatException(); + } // Return the metadata BdfDictionary meta = new BdfDictionary(); meta.put(MSG_KEY_TIMESTAMP, m.getTimestamp()); @@ -138,6 +150,7 @@ class PrivateMessageValidator implements MessageValidator { meta.put(MSG_KEY_MSG_TYPE, PRIVATE_MESSAGE); meta.put(MSG_KEY_HAS_TEXT, text != null); meta.put(MSG_KEY_ATTACHMENT_HEADERS, headers); + if (timer != null) meta.put(MSG_KEY_AUTO_DELETE_TIMER, timer); return new BdfMessageContext(meta); } diff --git a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java index e773d3195..bdac63239 100644 --- a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java @@ -58,6 +58,7 @@ import static java.util.Collections.emptyList; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.util.LogUtils.logException; @@ -351,14 +352,18 @@ public class TestDataCreatorImpl implements TestDataCreator { long timestamp = clock.currentTimeMillis() - num * 60 * 1000; String text = getRandomText(); boolean local = random.nextBoolean(); - createPrivateMessage(contactId, groupId, text, timestamp, local); + boolean autoDelete = random.nextBoolean(); + createPrivateMessage(contactId, groupId, text, timestamp, local, + autoDelete); } private void createPrivateMessage(ContactId contactId, GroupId groupId, - String text, long timestamp, boolean local) throws DbException { + String text, long timestamp, boolean local, boolean autoDelete) + throws DbException { + long timer = autoDelete ? MIN_AUTO_DELETE_TIMER_MS : -1; try { PrivateMessage m = privateMessageFactory.createPrivateMessage( - groupId, timestamp, text, emptyList()); + groupId, timestamp, text, emptyList(), timer); if (local) { messagingManager.addLocalMessage(m); } else { diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java index 8184cc168..a6ea35886 100644 --- a/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java @@ -19,6 +19,7 @@ import java.util.List; import javax.inject.Inject; +import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS; 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.record.Record.MAX_RECORD_PAYLOAD_BYTES; @@ -78,12 +79,12 @@ public class MessageSizeIntegrationTest extends BriarTestCase { getRandomString(MAX_CONTENT_TYPE_BYTES))); } PrivateMessage message = privateMessageFactory.createPrivateMessage( - groupId, timestamp, text, headers); + groupId, timestamp, text, headers, MAX_AUTO_DELETE_TIMER_MS); // Check the size of the serialised message int length = message.getMessage().getRawLength(); assertTrue(length > UniqueId.LENGTH + 8 + MAX_PRIVATE_MESSAGE_TEXT_LENGTH + MAX_ATTACHMENTS_PER_MESSAGE - * (UniqueId.LENGTH + MAX_CONTENT_TYPE_BYTES)); + * (UniqueId.LENGTH + MAX_CONTENT_TYPE_BYTES) + 4); assertTrue(length <= MAX_RECORD_PAYLOAD_BYTES); } diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java index 30c0e1d8f..405381299 100644 --- a/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java @@ -340,8 +340,9 @@ public class MessagingManagerIntegrationTest BriarIntegrationTestComponent to, @Nullable String text, List attachments) throws Exception { GroupId g = from.getMessagingManager().getConversationId(contactId); + // TODO: Add tests for auto-deletion timer PrivateMessage m = messageFactory.createPrivateMessage(g, - clock.currentTimeMillis(), text, attachments); + clock.currentTimeMillis(), text, attachments, -1); from.getMessagingManager().addLocalMessage(m); syncMessage(from, to, contactId, 1 + attachments.size(), true); return m; diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java index 88f1530a3..3c8ee93c9 100644 --- a/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java @@ -123,7 +123,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { PrivateMessageFactory privateMessageFactory = device.getPrivateMessageFactory(); PrivateMessage message = privateMessageFactory.createPrivateMessage( - groupId, timestamp, "Hi!", singletonList(attachmentHeader)); + groupId, timestamp, "Hi!", singletonList(attachmentHeader), -1); messagingManager.addLocalMessage(message); } diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt index 221b95001..c9634d192 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt @@ -41,7 +41,8 @@ internal class WebSocketControllerTest : ControllerTest() { true, true, true, - emptyList() + emptyList(), + -1 ) private val event = PrivateMessageReceivedEvent(header, contact.id) private val outputEvent = OutputEvent(EVENT_CONVERSATION_MESSAGE, event.output(text)) diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt index a3a9d49c4..8e20f9b1f 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt @@ -4,7 +4,13 @@ import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.NotFoundResponse import io.javalin.plugin.json.JavalinJson.toJson -import io.mockk.* +import io.mockk.CapturingSlot +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.runs import org.briarproject.bramble.api.contact.ContactId import org.briarproject.bramble.api.db.NoSuchContactException import org.briarproject.briar.api.identity.AuthorInfo @@ -62,7 +68,8 @@ internal class MessagingControllerImplTest : ControllerTest() { true, true, true, - emptyList() + emptyList(), + -1 ) private val sessionId = SessionId(getRandomId()) private val privateMessage = PrivateMessage(message)