Merge branch '1828-self-destruct-timer-private-messages' into '804-self-destructing-messages'

Update messaging client to include a self-destruct timer in each message

See merge request briar/briar!1299
This commit is contained in:
Torsten Grote
2020-11-19 16:57:51 +00:00
19 changed files with 343 additions and 86 deletions

View File

@@ -0,0 +1,23 @@
package org.briarproject.bramble.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

@@ -140,6 +140,7 @@ 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_ONLY;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -273,15 +274,11 @@ 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);
viewModel.hasImageSupport().observe(this, new Observer<Boolean>() { observeOnce(viewModel.getPrivateMessageFormat(), this, format -> {
@Override if (format != TEXT_ONLY) {
public void onChanged(@Nullable Boolean hasSupport) { // TODO: remove cast when removing feature flag
if (hasSupport != null && hasSupport) { ((TextAttachmentController) sendController)
// TODO: remove cast when removing feature flag .setImagesSupported();
((TextAttachmentController) sendController)
.setImagesSupported();
viewModel.hasImageSupport().removeObserver(this);
}
} }
}); });
} else { } else {
@@ -657,8 +654,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());

View File

@@ -32,6 +32,7 @@ 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;
@@ -52,17 +53,20 @@ import androidx.lifecycle.Transformations;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
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.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; 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.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 Logger LOG = private static final Logger LOG =
getLogger(ConversationViewModel.class.getName()); getLogger(ConversationViewModel.class.getName());
private static final String SHOW_ONBOARDING_IMAGE = private static final String SHOW_ONBOARDING_IMAGE =
@@ -89,7 +93,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<Boolean> imageSupport = private final MutableLiveData<PrivateMessageFormat> privateMessageFormat =
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveEvent<Boolean> showImageOnboarding = private final MutableLiveEvent<Boolean> showImageOnboarding =
new MutableLiveEvent<>(); new MutableLiveEvent<>();
@@ -210,11 +214,8 @@ public class ConversationViewModel extends AndroidViewModel
// messagingGroupId is loaded with the contact // messagingGroupId is loaded with the contact
observeForeverOnce(messagingGroupId, groupId -> { observeForeverOnce(messagingGroupId, groupId -> {
requireNonNull(groupId); requireNonNull(groupId);
observeForeverOnce(imageSupport, hasImageSupport -> { observeForeverOnce(privateMessageFormat, format ->
requireNonNull(hasImageSupport); createMessage(groupId, text, headers, timestamp, format));
createMessage(groupId, text, headers, timestamp,
hasImageSupport);
});
}); });
} }
@@ -244,10 +245,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 are supported // check if images and auto-deletion are supported
boolean imagesSupported = db.transactionWithResult(true, txn -> PrivateMessageFormat format = db.transactionWithResult(true, txn ->
messagingManager.contactSupportsImages(txn, c)); messagingManager.getContactMessageFormat(txn, c));
imageSupport.postValue(imagesSupported); privateMessageFormat.postValue(format);
// check if introductions are supported // check if introductions are supported
Collection<Contact> contacts = contactManager.getContacts(); Collection<Contact> contacts = contactManager.getContacts();
@@ -256,7 +257,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 (imagesSupported && if (format != TEXT_ONLY &&
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);
@@ -277,15 +278,19 @@ public class ConversationViewModel extends AndroidViewModel
@UiThread @UiThread
private void createMessage(GroupId groupId, @Nullable String text, private void createMessage(GroupId groupId, @Nullable String text,
List<AttachmentHeader> headers, long timestamp, List<AttachmentHeader> headers, long timestamp,
boolean hasImageSupport) { PrivateMessageFormat format) {
try { try {
PrivateMessage pm; PrivateMessage pm;
if (hasImageSupport) { if (format == TEXT_ONLY) {
pm = privateMessageFactory.createLegacyPrivateMessage(
groupId, timestamp, requireNonNull(text));
} else if (format == TEXT_IMAGES) {
pm = privateMessageFactory.createPrivateMessage(groupId, pm = privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers); timestamp, text, headers);
} else { } else {
pm = privateMessageFactory.createLegacyPrivateMessage( // TODO: Look up auto-delete timer
groupId, timestamp, requireNonNull(text)); pm = privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers, NO_AUTO_DELETE_TIMER);
} }
storeMessage(pm); storeMessage(pm);
} catch (FormatException e) { } catch (FormatException e) {
@@ -305,7 +310,8 @@ public class ConversationViewModel extends AndroidViewModel
PrivateMessageHeader h = new PrivateMessageHeader( PrivateMessageHeader h = new PrivateMessageHeader(
message.getId(), message.getGroupId(), message.getId(), message.getGroupId(),
message.getTimestamp(), true, true, false, false, message.getTimestamp(), true, true, false, false,
m.hasText(), m.getAttachmentHeaders()); m.hasText(), m.getAttachmentHeaders(),
m.getAutoDeleteTimer());
// TODO add text to cache when available here // TODO add text to cache when available here
addedHeader.postEvent(h); addedHeader.postEvent(h);
} catch (DbException e) { } catch (DbException e) {
@@ -330,8 +336,8 @@ public class ConversationViewModel extends AndroidViewModel
return contactName; return contactName;
} }
LiveData<Boolean> hasImageSupport() { LiveData<PrivateMessageFormat> getPrivateMessageFormat() {
return imageSupport; return privateMessageFormat;
} }
LiveEvent<Boolean> showImageOnboarding() { LiveEvent<Boolean> showImageOnboarding() {

View File

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

View File

@@ -8,44 +8,66 @@ 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.bramble.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 legacyFormat, hasText; private final boolean 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 legacy format, which does not * Constructor for private messages in the
* support attachments. * {@link PrivateMessageFormat#TEXT_ONLY TEXT_ONLY} format.
*/ */
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 current format, which supports * Constructor for private messages in the
* attachments. * {@link PrivateMessageFormat#TEXT_IMAGES TEXT_IMAGES} format.
*/ */
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;
legacyFormat = false; autoDeleteTimer = NO_AUTO_DELETE_TIMER;
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 boolean isLegacyFormat() { public PrivateMessageFormat getFormat() {
return legacyFormat; return format;
} }
public boolean hasText() { public boolean hasText() {
@@ -55,4 +77,8 @@ public class PrivateMessage {
public List<AttachmentHeader> getAttachmentHeaders() { public List<AttachmentHeader> getAttachmentHeaders() {
return attachmentHeaders; return attachmentHeaders;
} }
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
} }

View File

@@ -11,11 +11,29 @@ 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

@@ -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_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

@@ -16,13 +16,16 @@ public class PrivateMessageHeader extends ConversationMessageHeader {
private final boolean hasText; private final boolean hasText;
private final List<AttachmentHeader> attachmentHeaders; private final List<AttachmentHeader> attachmentHeaders;
private final long autoDeleteTimer;
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);
this.hasText = hasText; this.hasText = hasText;
this.attachmentHeaders = headers; this.attachmentHeaders = headers;
this.autoDeleteTimer = autoDeleteTimer;
} }
public boolean hasText() { public boolean hasText() {
@@ -33,9 +36,12 @@ public class PrivateMessageHeader extends ConversationMessageHeader {
return attachmentHeaders; return attachmentHeaders;
} }
public long getAutoDeleteTimer() {
return autoDeleteTimer;
}
@Override @Override
public <T> T accept(ConversationMessageVisitor<T> v) { public <T> T accept(ConversationMessageVisitor<T> v) {
return v.visitPrivateMessageHeader(this); return v.visitPrivateMessageHeader(this);
} }
} }

View File

@@ -13,4 +13,5 @@ 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

@@ -38,6 +38,7 @@ 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;
@@ -57,14 +58,19 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
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.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.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;
@@ -196,9 +202,11 @@ 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); read, false, false, hasText, headers, timer);
ContactId contactId = getContactId(txn, groupId); ContactId contactId = getContactId(txn, groupId);
PrivateMessageReceivedEvent event = PrivateMessageReceivedEvent event =
new PrivateMessageReceivedEvent(header, contactId); new PrivateMessageReceivedEvent(header, contactId);
@@ -234,7 +242,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
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.isLegacyFormat()) { if (m.getFormat() != TEXT_ONLY) {
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();
@@ -243,6 +251,12 @@ 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()) {
@@ -355,12 +369,14 @@ 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())); emptyList(), NO_AUTO_DELETE_TIMER));
} 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))); parseAttachmentHeaders(meta), timer));
} }
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
@@ -422,12 +438,13 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
} }
@Override @Override
public boolean contactSupportsImages(Transaction txn, ContactId c) public PrivateMessageFormat getContactMessageFormat(Transaction txn,
throws DbException { ContactId c) throws DbException {
int minorVersion = clientVersioningManager int minorVersion = clientVersioningManager
.getClientMinorVersion(txn, c, CLIENT_ID, 0); .getClientMinorVersion(txn, c, CLIENT_ID, 0);
// support was added in 0.1 if (minorVersion >= 3) return TEXT_IMAGES_AUTO_DELETE;
return minorVersion > 0; else if (minorVersion >= 1) return TEXT_IMAGES;
else return TEXT_ONLY;
} }
@Override @Override

View File

@@ -16,6 +16,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong; import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
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;
@@ -47,21 +48,43 @@ 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 {
// Validate the arguments validateTextAndAttachmentHeaders(text, headers);
if (text == null) { BdfList attachmentList = serialiseAttachmentHeaders(headers);
if (headers.isEmpty()) throw new IllegalArgumentException();
} else if (utf8IsTooLong(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH)) {
throw new IllegalArgumentException();
}
// Serialise the attachment headers
BdfList attachmentList = new BdfList();
for (AttachmentHeader a : headers) {
attachmentList.add(
BdfList.of(a.getMessageId(), a.getContentType()));
}
// Serialise the message // Serialise the message
BdfList body = BdfList.of(PRIVATE_MESSAGE, text, attachmentList); BdfList body = BdfList.of(PRIVATE_MESSAGE, text, attachmentList);
Message m = clientHelper.createMessage(groupId, timestamp, body); Message m = clientHelper.createMessage(groupId, timestamp, body);
return new PrivateMessage(m, text != null, headers); 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 (headers.isEmpty()) throw new IllegalArgumentException();
} else if (utf8IsTooLong(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH)) {
throw new IllegalArgumentException();
}
}
private BdfList serialiseAttachmentHeaders(List<AttachmentHeader> headers) {
BdfList attachmentList = new BdfList();
for (AttachmentHeader a : headers) {
attachmentList.add(
BdfList.of(a.getMessageId(), a.getContentType()));
}
return attachmentList;
}
} }

View File

@@ -23,6 +23,8 @@ import java.io.InputStream;
import javax.annotation.concurrent.Immutable; 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.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
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;
@@ -34,6 +36,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.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;
@@ -99,7 +102,7 @@ class PrivateMessageValidator implements MessageValidator {
private BdfMessageContext validateLegacyPrivateMessage(Message m, private BdfMessageContext validateLegacyPrivateMessage(Message m,
BdfList body) throws FormatException { BdfList body) throws FormatException {
// Private message text // Client version 0.0: 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);
@@ -113,8 +116,11 @@ class PrivateMessageValidator implements MessageValidator {
private BdfMessageContext validatePrivateMessage(Message m, BdfList body) private BdfMessageContext validatePrivateMessage(Message m, BdfList body)
throws FormatException { throws FormatException {
// Message type, optional private message text, attachment headers // Client version 0.1 to 0.2: Message type, optional private message
checkSize(body, 3); // text, attachment headers.
// 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);
@@ -129,6 +135,12 @@ 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 = 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 // 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());
@@ -137,6 +149,7 @@ 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 != null) meta.put(MSG_KEY_AUTO_DELETE_TIMER, timer);
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }

View File

@@ -54,6 +54,8 @@ import javax.inject.Inject;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; 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.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
@@ -316,13 +318,17 @@ public class TestDataCreatorImpl implements TestDataCreator {
long timestamp = clock.currentTimeMillis() - num * 60 * 1000; long timestamp = clock.currentTimeMillis() - num * 60 * 1000;
String text = getRandomText(); String text = getRandomText();
boolean local = random.nextBoolean(); boolean local = random.nextBoolean();
createPrivateMessage(groupId, text, timestamp, local); boolean autoDelete = random.nextBoolean();
createPrivateMessage(groupId, text, timestamp, local, autoDelete);
} }
private void createPrivateMessage(GroupId groupId, String text, private void createPrivateMessage(GroupId groupId, String text,
long timestamp, boolean local) throws DbException, FormatException { long timestamp, boolean local, boolean autoDelete)
PrivateMessage m = privateMessageFactory throws DbException, FormatException {
.createPrivateMessage(groupId, timestamp, text, emptyList()); long timer = autoDelete ?
MIN_AUTO_DELETE_TIMER_MS : NO_AUTO_DELETE_TIMER;
PrivateMessage m = privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, emptyList(), timer);
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put("timestamp", timestamp); meta.put("timestamp", timestamp);
meta.put("local", local); meta.put("local", local);

View File

@@ -19,6 +19,7 @@ import java.util.List;
import javax.inject.Inject; 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_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;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES; import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
@@ -77,12 +78,12 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
getRandomString(MAX_CONTENT_TYPE_BYTES))); getRandomString(MAX_CONTENT_TYPE_BYTES)));
} }
PrivateMessage message = privateMessageFactory.createPrivateMessage( PrivateMessage message = privateMessageFactory.createPrivateMessage(
groupId, timestamp, text, headers); groupId, timestamp, text, headers, MAX_AUTO_DELETE_TIMER_MS);
// Check the size of the serialised message // Check the size of the serialised message
int length = message.getMessage().getRawLength(); int length = message.getMessage().getRawLength();
assertTrue(length > UniqueId.LENGTH + 8 assertTrue(length > UniqueId.LENGTH + 8
+ MAX_PRIVATE_MESSAGE_TEXT_LENGTH + MAX_ATTACHMENTS_PER_MESSAGE + 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); assertTrue(length <= MAX_RECORD_PAYLOAD_BYTES);
} }

View File

@@ -32,6 +32,8 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet; import static java.util.Collections.emptySet;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
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.api.sync.validation.MessageState.PENDING; import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
@@ -89,7 +91,7 @@ public class MessagingManagerIntegrationTest
@Test @Test
public void testSimpleConversation() throws Exception { public void testSimpleConversation() throws Exception {
// conversation start out empty // conversation starts out empty
Collection<ConversationMessageHeader> messages0 = getMessages(c0); Collection<ConversationMessageHeader> messages0 = getMessages(c0);
Collection<ConversationMessageHeader> messages1 = getMessages(c1); Collection<ConversationMessageHeader> messages1 = getMessages(c1);
assertEquals(0, messages0.size()); assertEquals(0, messages0.size());
@@ -108,6 +110,10 @@ public class MessagingManagerIntegrationTest
(PrivateMessageHeader) messages1.iterator().next(); (PrivateMessageHeader) messages1.iterator().next();
assertTrue(m0.hasText()); assertTrue(m0.hasText());
assertTrue(m1.hasText()); assertTrue(m1.hasText());
assertEquals(0, m0.getAttachmentHeaders().size());
assertEquals(0, m1.getAttachmentHeaders().size());
assertEquals(NO_AUTO_DELETE_TIMER, m0.getAutoDeleteTimer());
assertEquals(NO_AUTO_DELETE_TIMER, m1.getAutoDeleteTimer());
assertTrue(m0.isRead()); assertTrue(m0.isRead());
assertFalse(m1.isRead()); assertFalse(m1.isRead());
assertGroupCounts(c0, 1, 0); assertGroupCounts(c0, 1, 0);
@@ -143,13 +149,44 @@ public class MessagingManagerIntegrationTest
assertFalse(m1.hasText()); assertFalse(m1.hasText());
assertEquals(1, m0.getAttachmentHeaders().size()); assertEquals(1, m0.getAttachmentHeaders().size());
assertEquals(1, m1.getAttachmentHeaders().size()); assertEquals(1, m1.getAttachmentHeaders().size());
assertEquals(NO_AUTO_DELETE_TIMER, m0.getAutoDeleteTimer());
assertEquals(NO_AUTO_DELETE_TIMER, m1.getAutoDeleteTimer());
assertTrue(m0.isRead());
assertFalse(m1.isRead());
assertGroupCounts(c0, 1, 0);
assertGroupCounts(c1, 1, 1);
}
@Test
public void testAutoDeleteTimer() throws Exception {
// send message with auto-delete timer
sendMessage(c0, c1, getRandomString(123), emptyList(),
MIN_AUTO_DELETE_TIMER_MS);
// message with timer is sent/displayed properly
Collection<ConversationMessageHeader> messages0 = getMessages(c0);
Collection<ConversationMessageHeader> messages1 = getMessages(c1);
assertEquals(1, messages0.size());
assertEquals(1, messages1.size());
PrivateMessageHeader m0 =
(PrivateMessageHeader) messages0.iterator().next();
PrivateMessageHeader m1 =
(PrivateMessageHeader) messages1.iterator().next();
assertTrue(m0.hasText());
assertTrue(m1.hasText());
assertEquals(0, m0.getAttachmentHeaders().size());
assertEquals(0, m1.getAttachmentHeaders().size());
assertEquals(MIN_AUTO_DELETE_TIMER_MS, m0.getAutoDeleteTimer());
assertEquals(MIN_AUTO_DELETE_TIMER_MS, m1.getAutoDeleteTimer());
assertTrue(m0.isRead());
assertFalse(m1.isRead());
assertGroupCounts(c0, 1, 0); assertGroupCounts(c0, 1, 0);
assertGroupCounts(c1, 1, 1); assertGroupCounts(c1, 1, 1);
} }
@Test @Test
public void testDeleteAll() throws Exception { public void testDeleteAll() throws Exception {
// send 3 message (1 with attachment) // send 3 messages (1 with attachment)
sendMessage(c0, c1, getRandomString(42)); sendMessage(c0, c1, getRandomString(42));
sendMessage(c0, c1, getRandomString(23)); sendMessage(c0, c1, getRandomString(23));
sendMessage(c0, c1, null, singletonList(addAttachment(c0))); sendMessage(c0, c1, null, singletonList(addAttachment(c0)));
@@ -331,17 +368,23 @@ public class MessagingManagerIntegrationTest
} }
private PrivateMessage sendMessage(BriarIntegrationTestComponent from, private PrivateMessage sendMessage(BriarIntegrationTestComponent from,
BriarIntegrationTestComponent to, String text) BriarIntegrationTestComponent to, String text) throws Exception {
throws Exception {
return sendMessage(from, to, text, emptyList()); return sendMessage(from, to, text, emptyList());
} }
private PrivateMessage sendMessage(BriarIntegrationTestComponent from, private PrivateMessage sendMessage(BriarIntegrationTestComponent from,
BriarIntegrationTestComponent to, @Nullable String text, BriarIntegrationTestComponent to, @Nullable String text,
List<AttachmentHeader> attachments) throws Exception { List<AttachmentHeader> attachments) throws Exception {
return sendMessage(from, to, text, attachments, NO_AUTO_DELETE_TIMER);
}
private PrivateMessage sendMessage(BriarIntegrationTestComponent from,
BriarIntegrationTestComponent to, @Nullable String text,
List<AttachmentHeader> attachments, long autoDeleteTimer)
throws Exception {
GroupId g = from.getMessagingManager().getConversationId(contactId); GroupId g = from.getMessagingManager().getConversationId(contactId);
PrivateMessage m = messageFactory.createPrivateMessage(g, PrivateMessage m = messageFactory.createPrivateMessage(g,
clock.currentTimeMillis(), text, attachments); clock.currentTimeMillis(), text, attachments, autoDeleteTimer);
from.getMessagingManager().addLocalMessage(m); from.getMessagingManager().addLocalMessage(m);
syncMessage(from, to, contactId, 1 + attachments.size(), true); syncMessage(from, to, contactId, 1 + attachments.size(), true);
return m; return m;

View File

@@ -20,6 +20,8 @@ import org.junit.Test;
import java.io.InputStream; import java.io.InputStream;
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.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
@@ -34,6 +36,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.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;
@@ -391,6 +394,48 @@ public class PrivateMessageValidatorTest extends BrambleMockTestCase {
validator.validateMessage(message, group); validator.validateMessage(message, group);
} }
@Test
public void testAcceptsNullTimerForPrivateMessage() throws Exception {
testAcceptsPrivateMessage(BdfList.of(PRIVATE_MESSAGE, text,
new BdfList(), null), noAttachmentsMeta);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsNonLongTimerForPrivateMessage() throws Exception {
testRejectsPrivateMessage(BdfList.of(PRIVATE_MESSAGE, text,
new BdfList(), "foo"));
}
@Test(expected = InvalidMessageException.class)
public void testRejectsTooSmallTimerForPrivateMessage() throws Exception {
testRejectsPrivateMessage(BdfList.of(PRIVATE_MESSAGE, text,
new BdfList(), MIN_AUTO_DELETE_TIMER_MS - 1));
}
@Test(expected = InvalidMessageException.class)
public void testRejectsTooBigTimerForPrivateMessage() throws Exception {
testRejectsPrivateMessage(BdfList.of(PRIVATE_MESSAGE, text,
new BdfList(), MAX_AUTO_DELETE_TIMER_MS + 1));
}
@Test
public void testAcceptsMinTimerForPrivateMessage() throws Exception {
BdfDictionary minTimerMeta = new BdfDictionary(noAttachmentsMeta);
minTimerMeta.put(MSG_KEY_AUTO_DELETE_TIMER, MIN_AUTO_DELETE_TIMER_MS);
testAcceptsPrivateMessage(BdfList.of(PRIVATE_MESSAGE, text,
new BdfList(), MIN_AUTO_DELETE_TIMER_MS), minTimerMeta);
}
@Test
public void testAcceptsMaxTimerForPrivateMessage() throws Exception {
BdfDictionary maxTimerMeta = new BdfDictionary(noAttachmentsMeta);
maxTimerMeta.put(MSG_KEY_AUTO_DELETE_TIMER, MAX_AUTO_DELETE_TIMER_MS);
testAcceptsPrivateMessage(BdfList.of(PRIVATE_MESSAGE, text,
new BdfList(), MAX_AUTO_DELETE_TIMER_MS), maxTimerMeta);
}
private void testRejectsLegacyMessage(BdfList body) throws Exception { private void testRejectsLegacyMessage(BdfList body) throws Exception {
expectCheckTimestamp(now); expectCheckTimestamp(now);
expectParseList(body); expectParseList(body);

View File

@@ -34,6 +34,7 @@ import java.util.concurrent.CountDownLatch;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
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.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID; import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
@@ -123,7 +124,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
PrivateMessageFactory privateMessageFactory = PrivateMessageFactory privateMessageFactory =
device.getPrivateMessageFactory(); device.getPrivateMessageFactory();
PrivateMessage message = privateMessageFactory.createPrivateMessage( PrivateMessage message = privateMessageFactory.createPrivateMessage(
groupId, timestamp, "Hi!", singletonList(attachmentHeader)); groupId, timestamp, "Hi!", singletonList(attachmentHeader),
NO_AUTO_DELETE_TIMER);
messagingManager.addLocalMessage(message); messagingManager.addLocalMessage(message);
} }

View File

@@ -6,6 +6,7 @@ import io.mockk.CapturingSlot
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import org.briarproject.bramble.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER
import org.briarproject.bramble.api.identity.AuthorInfo import org.briarproject.bramble.api.identity.AuthorInfo
import org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED import org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED
import org.briarproject.bramble.test.ImmediateExecutor import org.briarproject.bramble.test.ImmediateExecutor
@@ -41,7 +42,8 @@ internal class WebSocketControllerTest : ControllerTest() {
true, true,
true, true,
true, true,
emptyList() emptyList(),
NO_AUTO_DELETE_TIMER
) )
private val event = PrivateMessageReceivedEvent(header, contact.id) private val event = PrivateMessageReceivedEvent(header, contact.id)
private val outputEvent = OutputEvent(EVENT_CONVERSATION_MESSAGE, event.output(text)) private val outputEvent = OutputEvent(EVENT_CONVERSATION_MESSAGE, event.output(text))

View File

@@ -4,7 +4,14 @@ import io.javalin.http.BadRequestResponse
import io.javalin.http.Context import io.javalin.http.Context
import io.javalin.http.NotFoundResponse import io.javalin.http.NotFoundResponse
import io.javalin.plugin.json.JavalinJson.toJson 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.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER
import org.briarproject.bramble.api.contact.ContactId import org.briarproject.bramble.api.contact.ContactId
import org.briarproject.bramble.api.db.NoSuchContactException import org.briarproject.bramble.api.db.NoSuchContactException
import org.briarproject.bramble.api.identity.AuthorInfo import org.briarproject.bramble.api.identity.AuthorInfo
@@ -62,7 +69,8 @@ internal class MessagingControllerImplTest : ControllerTest() {
true, true,
true, true,
true, true,
emptyList() emptyList(),
NO_AUTO_DELETE_TIMER
) )
private val sessionId = SessionId(getRandomId()) private val sessionId = SessionId(getRandomId())
private val privateMessage = PrivateMessage(message) private val privateMessage = PrivateMessage(message)