From f76f9be4edb35473671e85f54cc1db7f647c8a5e Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Mon, 18 Feb 2019 15:05:04 -0300 Subject: [PATCH] Reject attachments that exceed the allowed size Closes #1468 --- .../conversation/AttachmentResult.java | 43 +++++++++++++++++++ .../conversation/ConversationViewModel.java | 20 +++++++-- .../briar/android/view/ImagePreviewItem.java | 2 +- .../view/TextAttachmentController.java | 38 +++++++++++----- briar-android/src/main/res/values/strings.xml | 1 + .../api/messaging/FileTooBigException.java | 6 +++ .../api/messaging/MessagingConstants.java | 3 +- .../briar/api/messaging/MessagingManager.java | 2 + .../briar/messaging/MessagingManagerImpl.java | 40 +++++++++++------ 9 files changed, 125 insertions(+), 30 deletions(-) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentResult.java create mode 100644 briar-api/src/main/java/org/briarproject/briar/api/messaging/FileTooBigException.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentResult.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentResult.java new file mode 100644 index 0000000000..926d32fab3 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentResult.java @@ -0,0 +1,43 @@ +package org.briarproject.briar.android.conversation; + +import android.net.Uri; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +public class AttachmentResult { + + @Nullable + private final Uri uri; + @Nullable + private final String errorMsg; + + public AttachmentResult(Uri uri) { + this.uri = uri; + this.errorMsg = null; + } + + public AttachmentResult(@Nullable String errorMsg) { + this.uri = null; + this.errorMsg = errorMsg; + } + + @Nullable + public Uri getUri() { + return uri; + } + + public boolean isError() { + return errorMsg != null; + } + + @Nullable + public String getErrorMsg() { + return errorMsg; + } + +} 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 b428ee901e..3049339433 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 @@ -27,11 +27,13 @@ import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.system.AndroidExecutor; +import org.briarproject.briar.R; import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.view.TextAttachmentController.AttachmentManager; import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.api.messaging.AttachmentHeader; +import org.briarproject.briar.api.messaging.FileTooBigException; import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.PrivateMessage; import org.briarproject.briar.api.messaging.PrivateMessageFactory; @@ -54,6 +56,7 @@ import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.briar.android.conversation.AttachmentDimensions.getAttachmentDimensions; 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.MessagingConstants.MAX_IMAGE_SIZE; @NotNullByDefault public class ConversationViewModel extends AndroidViewModel implements @@ -192,9 +195,11 @@ public class ConversationViewModel extends AndroidViewModel implements } @Override - public void storeAttachment(Uri uri, boolean needsSize, Runnable onSuccess, - Runnable onError) { + public LiveData<AttachmentResult> storeAttachment(Uri uri, + boolean needsSize) { if (messagingGroupId.getValue() == null) loadGroupId(); + // use LiveData to not keep references to view scope + MutableLiveData<AttachmentResult> result = new MutableLiveData<>(); observeForeverOnce(messagingGroupId, groupId -> dbExecutor.execute(() -> { if (groupId == null) throw new IllegalStateException(); @@ -204,13 +209,20 @@ public class ConversationViewModel extends AndroidViewModel implements getApplication().getContentResolver(); attachmentController.createAttachmentHeader(contentResolver, groupId, uri, needsSize); - androidExecutor.runOnUiThread(onSuccess); + result.postValue(new AttachmentResult(uri)); + } catch(FileTooBigException e) { + logException(LOG, WARNING, e); + int mb = MAX_IMAGE_SIZE / 1024 / 1024; + String errorMsg = getApplication() + .getString(R.string.image_attach_error_too_big, mb); + result.postValue(new AttachmentResult(errorMsg)); } catch (DbException | IOException e) { logException(LOG, WARNING, e); - androidExecutor.runOnUiThread(onError); + result.postValue(new AttachmentResult((String) null)); } logDuration(LOG, "Storing attachment", start); })); + return result; } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewItem.java b/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewItem.java index 71576015fb..bf1ba71fb9 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewItem.java @@ -15,7 +15,7 @@ class ImagePreviewItem { private final Uri uri; private boolean waitForLoading = true; - private ImagePreviewItem(Uri uri) { + ImagePreviewItem(Uri uri) { this.uri = uri; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java index 0cc1cb32eb..db8723caff 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java @@ -1,6 +1,8 @@ package org.briarproject.briar.android.view; import android.app.Activity; +import android.arch.lifecycle.LifecycleOwner; +import android.arch.lifecycle.LiveData; import android.content.ClipData; import android.content.Context; import android.content.Intent; @@ -15,6 +17,7 @@ import android.widget.Toast; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.R; +import org.briarproject.briar.android.conversation.AttachmentResult; import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener; import org.briarproject.briar.api.messaging.AttachmentHeader; @@ -149,9 +152,17 @@ public class TextAttachmentController extends TextSendController // store attachments and show preview when successful boolean needsSize = items.size() == 1; for (ImagePreviewItem item : items) { - attachmentManager.storeAttachment(item.getUri(), needsSize, - () -> imagePreview.loadPreviewImage(item), - this::onError); + attachmentManager.storeAttachment(item.getUri(), needsSize) + .observe(imageListener, this::onAttachmentResultReceived); + } + } + + private void onAttachmentResultReceived(AttachmentResult result) { + if (result.isError() || result.getUri() == null) { + onError(result.getErrorMsg()); + } else { + ImagePreviewItem item = new ImagePreviewItem(result.getUri()); + imagePreview.loadPreviewImage(item); } } @@ -194,8 +205,16 @@ public class TextAttachmentController extends TextSendController @Override public void onError() { - Toast.makeText(textInput.getContext(), R.string.image_attach_error, - LENGTH_LONG).show(); + onError(null); + } + + @UiThread + private void onError(@Nullable String errorMsg) { + if (errorMsg == null) { + errorMsg = imagePreview.getContext() + .getString(R.string.image_attach_error); + } + Toast.makeText(textInput.getContext(), errorMsg, LENGTH_LONG).show(); onCancel(); } @@ -268,7 +287,7 @@ public class TextAttachmentController extends TextSendController }; } - public interface AttachImageListener { + public interface AttachImageListener extends LifecycleOwner { void onAttachImage(Intent intent); } @@ -277,11 +296,10 @@ public class TextAttachmentController extends TextSendController * Stores a new attachment in the database. * * @param uri The Uri of the attachment to store. - * @param onSuccess will be run on the UiThread when the attachment was stored successfully. - * @param onError will be run on the UiThread when the attachment could not be stored. + * @param needsSize true if this is the only image in the message + * and therefore needs to know its size. */ - void storeAttachment(Uri uri, boolean needsSize, Runnable onSuccess, - Runnable onError); + LiveData<AttachmentResult> storeAttachment(Uri uri, boolean needsSize); List<AttachmentHeader> getAttachmentHeaders(); diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 4565d2a551..7c397a3516 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -130,6 +130,7 @@ <string name="image_caption_hint">Add a caption (optional)</string> <string name="image_attach">Attach image</string> <string name="image_attach_error">Could not attach image(s)</string> + <string name="image_attach_error_too_big">Image too big. Limit is %d MB.</string> <string name="set_contact_alias">Change contact name</string> <string name="set_contact_alias_hint">Contact name</string> <string name="set_alias_button">Change</string> diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/FileTooBigException.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/FileTooBigException.java new file mode 100644 index 0000000000..f7d9c7c04c --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/FileTooBigException.java @@ -0,0 +1,6 @@ +package org.briarproject.briar.api.messaging; + +import java.io.IOException; + +public class FileTooBigException extends IOException { +} diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java index c6114dc66d..c1be55bdf6 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java @@ -20,7 +20,8 @@ public interface MessagingConstants { /** * The maximum allowed size of image attachments. + * TODO: Different limit for GIFs? */ - int MAX_IMAGE_SIZE = 6 * 1024 * 1024; + int MAX_IMAGE_SIZE = MAX_MESSAGE_BODY_LENGTH; // 6 * 1024 * 1024; } 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 d3ba4acf1c..d069e337b9 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 @@ -37,6 +37,8 @@ public interface MessagingManager extends ConversationClient { /** * Stores a local attachment message. + * + * @throws FileTooBigException */ AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp, String contentType, InputStream is) throws DbException, IOException; 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 e73dce953a..8917be0368 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 @@ -18,14 +18,17 @@ import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group.Visibility; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook; +import org.briarproject.bramble.util.IoUtils; import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.conversation.ConversationMessageHeader; import org.briarproject.briar.api.messaging.Attachment; import org.briarproject.briar.api.messaging.AttachmentHeader; +import org.briarproject.briar.api.messaging.FileTooBigException; import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.PrivateMessage; import org.briarproject.briar.api.messaging.PrivateMessageHeader; @@ -33,18 +36,18 @@ import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent; import org.briarproject.briar.client.ConversationClientImpl; import java.io.ByteArrayInputStream; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Map; -import java.util.Random; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; import static java.util.Collections.emptyList; -import static org.briarproject.bramble.util.StringUtils.fromHexString; +import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; @Immutable @@ -55,15 +58,18 @@ class MessagingManagerImpl extends ConversationClientImpl private final ClientVersioningManager clientVersioningManager; private final ContactGroupFactory contactGroupFactory; + private final MessageFactory messageFactory; @Inject MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper, ClientVersioningManager clientVersioningManager, MetadataParser metadataParser, MessageTracker messageTracker, - ContactGroupFactory contactGroupFactory) { + ContactGroupFactory contactGroupFactory, + MessageFactory messageFactory) { super(db, clientHelper, metadataParser, messageTracker); this.clientVersioningManager = clientVersioningManager; this.contactGroupFactory = contactGroupFactory; + this.messageFactory = messageFactory; } @Override @@ -158,17 +164,25 @@ class MessagingManagerImpl extends ConversationClientImpl @Override public AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp, - String contentType, InputStream is) throws IOException { + String contentType, InputStream is) + throws DbException, IOException { // TODO add real implementation - if (is.available() == 0) throw new IOException(); - byte[] b = new byte[MessageId.LENGTH]; - new Random().nextBytes(b); - return new AttachmentHeader(new MessageId(b), contentType); + byte[] body = new byte[MAX_MESSAGE_BODY_LENGTH]; + try { + IoUtils.read(is, body); + } catch (EOFException ignored) { + } + if (is.available() > 0) throw new FileTooBigException(); + is.close(); + Message m = messageFactory.createMessage(groupId, timestamp, body); + clientHelper.addLocalMessage(m, new BdfDictionary(), false); + return new AttachmentHeader(m.getId(), contentType); } @Override public void removeAttachment(AttachmentHeader header) throws DbException { - // TODO add real implementation + db.transaction(false, + txn -> db.removeMessage(txn, header.getMessageId())); } private ContactId getContactId(Transaction txn, GroupId g) @@ -247,12 +261,10 @@ class MessagingManagerImpl extends ConversationClientImpl } @Override - public Attachment getAttachment(MessageId m) { + public Attachment getAttachment(MessageId mId) throws DbException { // TODO add real implementation - byte[] bytes = fromHexString("89504E470D0A1A0A0000000D49484452" + - "000000010000000108060000001F15C4" + - "890000000A49444154789C6300010000" + - "0500010D0A2DB40000000049454E44AE426082"); + Message m = clientHelper.getMessage(mId); + byte[] bytes = m.getBody(); return new Attachment(new ByteArrayInputStream(bytes)); } -- GitLab