diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt index 3a00df806..1cd3e76ea 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt @@ -229,7 +229,7 @@ class IconPickerActivity : LockingActivity() { if (documentFile.length() > MAX_ICON_SIZE) { iconCustomState.errorStringId = R.string.error_file_to_big } else { - mDatabase?.buildNewCustomIcon() { customIcon, binary -> + mDatabase?.buildNewCustomIcon { customIcon, binary -> if (customIcon != null) { iconCustomState.iconCustom = customIcon mDatabase?.let { database -> @@ -241,7 +241,7 @@ class IconPickerActivity : LockingActivity() { when { binary == null -> { } - binary.getSize(database.binaryCache) <= 0 -> { + binary.getSize() <= 0 -> { } database.isCustomIconBinaryDuplicate(binary) -> { iconCustomState.errorStringId = R.string.error_duplicate_file diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt index 7d1b625fa..b0d27eab8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt @@ -69,9 +69,7 @@ class ImageViewerActivity : LockingActivity() { supportActionBar?.title = attachment.name - val size = mDatabase?.binaryCache?.let { - attachment.binaryData.getSize(it) - } ?: 0L + val size = attachment.binaryData.getSize() supportActionBar?.subtitle = Formatter.formatFileSize(this, size) mDatabase?.let { database -> diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt index 493262d91..cab97406b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt @@ -126,9 +126,7 @@ class EntryAttachmentsItemsAdapter(context: Context) holder.binaryFileTitle.setTextColor(mTitleColor) } - val size = database?.binaryCache?.let { - entryAttachmentState.attachment.binaryData.getSize(it) - } ?: 0L + val size = entryAttachmentState.attachment.binaryData.getSize() holder.binaryFileSize.text = Formatter.formatFileSize(context, size) holder.binaryFileCompression.apply { if (entryAttachmentState.attachment.binaryData.isCompressed) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryByte.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryByte.kt index 35b45d8b2..bf1447a7e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryByte.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryByte.kt @@ -60,17 +60,12 @@ class BinaryByte : BinaryData { @Throws(IOException::class) override fun getInputDataStream(binaryCache: BinaryCache): InputStream { - return when { - getSize(binaryCache) > 0 -> { - Base64InputStream(ByteArrayInputStream(getByteArray(binaryCache)), Base64.NO_WRAP) - } - else -> ByteArrayInputStream(ByteArray(0)) - } + return Base64InputStream(ByteArrayInputStream(getByteArray(binaryCache)), Base64.NO_WRAP) } @Throws(IOException::class) override fun getOutputDataStream(binaryCache: BinaryCache): OutputStream { - return Base64OutputStream(ByteOutputStream(binaryCache), Base64.NO_WRAP) + return BinaryCountingOutputStream(Base64OutputStream(ByteOutputStream(binaryCache), Base64.NO_WRAP)) } @Throws(IOException::class) @@ -105,14 +100,6 @@ class BinaryByte : BinaryData { return getByteArray(binaryCache).isNotEmpty() } - override fun getSize(binaryCache: BinaryCache): Long { - return getByteArray(binaryCache).size.toLong() - } - - override fun binaryHash(binaryCache: BinaryCache): Int { - return getByteArray(binaryCache).contentHashCode() - } - @Throws(IOException::class) override fun clear(binaryCache: BinaryCache) { binaryCache.removeByteArray(mDataByteId) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryData.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryData.kt index 4ef115b0a..c9ffccae9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryData.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryData.kt @@ -23,9 +23,12 @@ import android.app.ActivityManager import android.content.Context import android.os.Parcel import android.os.Parcelable +import org.apache.commons.io.output.CountingOutputStream import java.io.IOException import java.io.InputStream import java.io.OutputStream +import java.nio.ByteBuffer +import java.security.MessageDigest import java.util.zip.GZIPInputStream import java.util.zip.GZIPOutputStream @@ -36,22 +39,30 @@ abstract class BinaryData : Parcelable { var isProtected: Boolean = false protected set var isCorrupted: Boolean = false + private var mLength: Long = 0 + private var mBinaryHash = 0 protected constructor(compressed: Boolean = false, protected: Boolean = false) { this.isCompressed = compressed this.isProtected = protected + this.mLength = 0 + this.mBinaryHash = 0 } protected constructor(parcel: Parcel) { isCompressed = parcel.readByte().toInt() != 0 isProtected = parcel.readByte().toInt() != 0 isCorrupted = parcel.readByte().toInt() != 0 + mLength = parcel.readLong() + mBinaryHash = parcel.readInt() } override fun writeToParcel(dest: Parcel, flags: Int) { dest.writeByte((if (isCompressed) 1 else 0).toByte()) dest.writeByte((if (isProtected) 1 else 0).toByte()) dest.writeByte((if (isCorrupted) 1 else 0).toByte()) + dest.writeLong(mLength) + dest.writeInt(mBinaryHash) } @Throws(IOException::class) @@ -85,13 +96,19 @@ abstract class BinaryData : Parcelable { abstract fun decompress(binaryCache: BinaryCache) @Throws(IOException::class) - abstract fun dataExists(binaryCache: BinaryCache): Boolean + open fun dataExists(binaryCache: BinaryCache): Boolean { + return mLength > 0 + } @Throws(IOException::class) - abstract fun getSize(binaryCache: BinaryCache): Long + fun getSize(): Long { + return mLength + } @Throws(IOException::class) - abstract fun binaryHash(binaryCache: BinaryCache): Int + fun binaryHash(): Int { + return mBinaryHash + } @Throws(IOException::class) abstract fun clear(binaryCache: BinaryCache) @@ -115,9 +132,52 @@ abstract class BinaryData : Parcelable { var result = isCompressed.hashCode() result = 31 * result + isProtected.hashCode() result = 31 * result + isCorrupted.hashCode() + result = 31 * result + mLength.hashCode() + result = 31 * result + mBinaryHash return result } + + /** + * Custom OutputStream to calculate the size and hash of binary file + */ + protected inner class BinaryCountingOutputStream(out: OutputStream): CountingOutputStream(out) { + + private val mMessageDigest: MessageDigest + init { + mLength = 0 + mMessageDigest = MessageDigest.getInstance("MD5") + mBinaryHash = 0 + } + + override fun beforeWrite(n: Int) { + super.beforeWrite(n) + mLength = byteCount + } + + override fun write(idx: Int) { + super.write(idx) + mMessageDigest.update(idx.toByte()) + } + + override fun write(bts: ByteArray) { + super.write(bts) + mMessageDigest.update(bts) + } + + override fun write(bts: ByteArray, st: Int, end: Int) { + super.write(bts, st, end) + mMessageDigest.update(bts, st, end) + } + + override fun close() { + super.close() + mLength = byteCount + val bytes = mMessageDigest.digest() + mBinaryHash = ByteBuffer.wrap(bytes).int + } + } + companion object { private val TAG = BinaryData::class.java.name diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryFile.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryFile.kt index e4ad26528..ddba0e3ec 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryFile.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryFile.kt @@ -25,10 +25,7 @@ import android.util.Base64 import android.util.Base64InputStream import android.util.Base64OutputStream import com.kunzisoft.keepass.stream.readAllBytes -import org.apache.commons.io.output.CountingOutputStream import java.io.* -import java.nio.ByteBuffer -import java.security.MessageDigest import java.util.zip.GZIPOutputStream import javax.crypto.Cipher import javax.crypto.CipherInputStream @@ -37,8 +34,6 @@ import javax.crypto.spec.IvParameterSpec class BinaryFile : BinaryData { - private var mLength: Long = 0 - private var mBinaryHash = 0 private var mDataFile: File? = null // Cipher to encrypt temp file @@ -50,14 +45,10 @@ class BinaryFile : BinaryData { constructor(dataFile: File, compressed: Boolean = false, protected: Boolean = false) : super(compressed, protected) { - this.mLength = 0 - this.mBinaryHash = 0 this.mDataFile = dataFile } constructor(parcel: Parcel) : super(parcel) { - mLength = parcel.readLong() - mBinaryHash = parcel.readInt() parcel.readString()?.let { mDataFile = File(it) } @@ -65,8 +56,6 @@ class BinaryFile : BinaryData { override fun writeToParcel(dest: Parcel, flags: Int) { super.writeToParcel(dest, flags) - dest.writeLong(mLength) - dest.writeInt(mBinaryHash) dest.writeString(mDataFile?.absolutePath) } @@ -154,15 +143,7 @@ class BinaryFile : BinaryData { } override fun dataExists(binaryCache: BinaryCache): Boolean { - return mDataFile != null && mLength > 0 - } - - override fun getSize(binaryCache: BinaryCache): Long { - return mLength - } - - override fun binaryHash(binaryCache: BinaryCache): Int { - return mBinaryHash + return mDataFile != null && super.dataExists(binaryCache) } override fun clear(binaryCache: BinaryCache) { @@ -185,51 +166,9 @@ class BinaryFile : BinaryData { override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + (mDataFile?.hashCode() ?: 0) - result = 31 * result + mLength.hashCode() - result = 31 * result + mBinaryHash return result } - /** - * Custom OutputStream to calculate the size and hash of binary file - */ - private inner class BinaryCountingOutputStream(out: OutputStream): CountingOutputStream(out) { - - private val mMessageDigest: MessageDigest - init { - mLength = 0 - mMessageDigest = MessageDigest.getInstance("MD5") - mBinaryHash = 0 - } - - override fun beforeWrite(n: Int) { - super.beforeWrite(n) - mLength = byteCount - } - - override fun write(idx: Int) { - super.write(idx) - mMessageDigest.update(idx.toByte()) - } - - override fun write(bts: ByteArray) { - super.write(bts) - mMessageDigest.update(bts) - } - - override fun write(bts: ByteArray, st: Int, end: Int) { - super.write(bts, st, end) - mMessageDigest.update(bts, st, end) - } - - override fun close() { - super.close() - mLength = byteCount - val bytes = mMessageDigest.digest() - mBinaryHash = ByteBuffer.wrap(bytes).int - } - } - companion object { private val TAG = BinaryFile::class.java.name diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryPool.kt index 4d08c8910..b18728477 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryPool.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryPool.kt @@ -120,11 +120,11 @@ abstract class BinaryPool(private val mBinaryCache: BinaryCache) { fun isBinaryDuplicate(binaryData: BinaryData?): Boolean { try { binaryData?.let { - if (it.getSize(mBinaryCache) > 0) { - val searchBinaryMD5 = it.binaryHash(mBinaryCache) + if (it.getSize() > 0) { + val searchBinaryMD5 = it.binaryHash() var i = 0 for ((_, binary) in pool) { - if (binary.binaryHash(mBinaryCache) == searchBinaryMD5) { + if (binary.binaryHash() == searchBinaryMD5) { i++ if (i > 1) return true @@ -164,10 +164,10 @@ abstract class BinaryPool(private val mBinaryCache: BinaryCache) { // Don't deduplicate val existentBinary = try { - if (binary.getSize(mBinaryCache) > 0) { + if (binary.getSize() > 0) { keyBinaryList.find { - val hash0 = it.binary.binaryHash(mBinaryCache) - val hash1 = binary.binaryHash(mBinaryCache) + val hash0 = it.binary.binaryHash() + val hash1 = binary.binaryHash() hash0 != 0 && hash1 != 0 && hash0 == hash1 } } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index 817d26018..158dd8206 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -297,10 +297,7 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte var size = 0L for ((label, poolId) in binaries) { size += label.length.toLong() - val binarySize = mDatabase?.binaryCache?.let { - attachmentPool[poolId]?.getSize(it) - } - size += binarySize ?: 0 + size += attachmentPool[poolId]?.getSize() ?: 0 } return size } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt index a6e0f488c..d52e9d67d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt @@ -144,7 +144,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, // Write type binary dataOutputStream.writeByte(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary) // Write size - dataOutputStream.writeUInt(UnsignedInt.fromKotlinLong(binary.getSize(binaryCache) + 1)) + dataOutputStream.writeUInt(UnsignedInt.fromKotlinLong(binary.getSize() + 1)) // Write protected flag var flag = DatabaseHeaderKDBX.KdbxBinaryFlags.None if (binary.isProtected) { @@ -498,7 +498,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX, mDatabaseKDBX.attachmentPool.doForEachOrderedBinaryWithoutDuplication { index, binary -> xml.startTag(null, DatabaseKDBXXML.ElemBinary) xml.attribute(null, DatabaseKDBXXML.AttrId, index.toString()) - if (binary.getSize(binaryCache) > 0) { + if (binary.getSize() > 0) { if (binary.isCompressed) { xml.attribute(null, DatabaseKDBXXML.AttrCompressed, DatabaseKDBXXML.ValTrue) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt index 709babca7..7ec4e151e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/EntryOutputKDB.kt @@ -95,7 +95,7 @@ class EntryOutputKDB(private val mDatabase: DatabaseKDB, // Binary mOutputStream.write(BINARY_DATA_FIELD_TYPE) val binaryData = mEntry.getBinary(mDatabase.attachmentPool) - val binaryDataLength = binaryData?.getSize(mDatabase.binaryCache) ?: 0L + val binaryDataLength = binaryData?.getSize() ?: 0L // Write data length mOutputStream.write(uIntTo4Bytes(UnsignedInt.fromKotlinLong(binaryDataLength))) // Write data diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/BinaryDatabaseManager.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/BinaryDatabaseManager.kt index 632c7844c..978fc2626 100644 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/BinaryDatabaseManager.kt +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/BinaryDatabaseManager.kt @@ -40,7 +40,7 @@ object BinaryDatabaseManager { update: ((percent: Int)->Unit)? = null, canceled: ()-> Boolean = { false }, bufferSize: Int = DEFAULT_BUFFER_SIZE) { - val fileSize = binaryData.getSize(binaryCache) + val fileSize = binaryData.getSize() var dataDownloaded = 0L binaryData.getUnGzipInputDataStream(binaryCache).use { inputStream -> inputStream.readAllBytes(bufferSize, canceled) { buffer ->