diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MatrixDigestCheckInputStream.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MatrixDigestCheckInputStream.kt new file mode 100644 index 0000000000..2a6ec59f5f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MatrixDigestCheckInputStream.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.crypto.attachments + +import android.util.Base64 +import java.io.FilterInputStream +import java.io.IOException +import java.io.InputStream +import java.security.MessageDigest + +class MatrixDigestCheckInputStream(`in`: InputStream?, val expectedDigest: String) : FilterInputStream(`in`) { + + val digest = MessageDigest.getInstance("SHA-256") + + @Throws(IOException::class) + override fun read(): Int { + val b = `in`.read() + if (b >= 0) { + digest.update(b.toByte()) + } + + if (b == -1) { + ensureDigest() + } + return b + } + + @Throws(IOException::class) + override fun read( + b: ByteArray, + off: Int, + len: Int): Int { + val n = `in`.read(b, off, len) + if (n > 0) { + digest.update(b, off, n) + } + + if (n == -1) { + ensureDigest() + } + return n + } + + @Throws(IOException::class) + private fun ensureDigest() { + val currentDigestValue = MXEncryptedAttachments.base64ToUnpaddedBase64(Base64.encodeToString(digest.digest(), Base64.DEFAULT)) + if (currentDigestValue != expectedDigest) { + throw IOException("Bad digest") + } + } +} diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt index 9ac8a4d3bc..8447557fe2 100644 --- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt +++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt @@ -84,10 +84,14 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde override fun cancel() { if (stream != null) { try { + // This is often called on main thread, and this could be a network Stream.. + // on close will throw android.os.NetworkOnMainThreadException, so we catch throwable stream?.close() // interrupts decode if any stream = null - } catch (ignore: IOException) { + } catch (ignore: Throwable) { Timber.e(ignore) + } finally { + stream = null } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt index f149b440e0..0bd59bf2fc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt @@ -75,7 +75,7 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, is ContentUploadStateTracker.State.Idle -> handleIdle() is ContentUploadStateTracker.State.EncryptingThumbnail -> handleEncryptingThumbnail() is ContentUploadStateTracker.State.UploadingThumbnail -> handleProgressThumbnail(state) - is ContentUploadStateTracker.State.Encrypting -> handleEncrypting() + is ContentUploadStateTracker.State.Encrypting -> handleEncrypting(state) is ContentUploadStateTracker.State.Uploading -> handleProgress(state) is ContentUploadStateTracker.State.Failure -> handleFailure(state) is ContentUploadStateTracker.State.Success -> handleSuccess() @@ -98,26 +98,28 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, } private fun handleEncryptingThumbnail() { - doHandleEncrypting(R.string.send_file_step_encrypting_thumbnail) + doHandleEncrypting(R.string.send_file_step_encrypting_thumbnail, 0, 0) } private fun handleProgressThumbnail(state: ContentUploadStateTracker.State.UploadingThumbnail) { doHandleProgress(R.string.send_file_step_sending_thumbnail, state.current, state.total) } - private fun handleEncrypting() { - doHandleEncrypting(R.string.send_file_step_encrypting_file) + private fun handleEncrypting(state: ContentUploadStateTracker.State.Encrypting) { + doHandleEncrypting(R.string.send_file_step_encrypting_file, state.current, state.total) } private fun handleProgress(state: ContentUploadStateTracker.State.Uploading) { doHandleProgress(R.string.send_file_step_sending_file, state.current, state.total) } - private fun doHandleEncrypting(resId: Int) { + private fun doHandleEncrypting(resId: Int, current: Long, total: Long) { progressLayout.visibility = View.VISIBLE + val percent = if (total > 0) (100L * (current.toFloat() / total.toFloat())) else 0f val progressBar = progressLayout.findViewById(R.id.mediaProgressBar) val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView) - progressBar?.isIndeterminate = true + progressBar?.isIndeterminate = false + progressBar?.progress = percent.toInt() progressTextView?.text = progressLayout.context.getString(resId) progressTextView?.setTextColor(messageColorProvider.getMessageTextColor(SendState.ENCRYPTING)) }