diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt index a5c10b9bf..74ad9f3a0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt @@ -25,6 +25,7 @@ import com.kunzisoft.keepass.app.database.CipherDatabaseAction import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.database.BinaryData import com.kunzisoft.keepass.database.exception.LoadDatabaseException import com.kunzisoft.keepass.model.MainCredential import com.kunzisoft.keepass.settings.PreferencesUtil @@ -55,6 +56,9 @@ class LoadDatabaseRunnable(private val context: Context, mReadonly, context.contentResolver, UriUtil.getBinaryDir(context), + { memoryWanted -> + BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted) + }, Database.LoadedKey.generateNewCipherKey(), mFixDuplicateUUID, progressTaskUpdater) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt index 84e7829cc..d5d39bb45 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.action import android.content.Context import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.database.BinaryData import com.kunzisoft.keepass.database.exception.LoadDatabaseException import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable @@ -46,6 +47,9 @@ class ReloadDatabaseRunnable(private val context: Context, try { mDatabase.reloadData(context.contentResolver, UriUtil.getBinaryDir(context), + { memoryWanted -> + BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted) + }, tempCipherKey ?: Database.LoadedKey.generateNewCipherKey(), progressTaskUpdater) } catch (e: LoadDatabaseException) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt index cfc4690b5..37dd9b7ca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt @@ -30,12 +30,15 @@ data class Attachment(var name: String, constructor(parcel: Parcel) : this( parcel.readString() ?: "", - parcel.readParcelable(BinaryData::class.java.classLoader) ?: BinaryByte() + // TODO BinaryParcelable + //parcel.readParcelable(BinaryData::class.java.classLoader) ?: + BinaryByte() ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(name) - parcel.writeParcelable(binaryData, flags) + // TODO BinaryParcelable + //parcel.writeParcelable(binaryData, flags) } override fun describeContents(): Int { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 2258417fb..20c7e658a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -453,6 +453,7 @@ class Database { readOnly: Boolean, contentResolver: ContentResolver, cacheDirectory: File, + isRAMSufficient: (memoryWanted: Long) -> Boolean, tempCipherKey: LoadedKey, fixDuplicateUUID: Boolean, progressTaskUpdater: ProgressTaskUpdater?) { @@ -474,7 +475,7 @@ class Database { // Read database stream for the first time readDatabaseStream(contentResolver, uri, { databaseInputStream -> - DatabaseInputKDB(cacheDirectory) + DatabaseInputKDB(cacheDirectory, isRAMSufficient) .openDatabase(databaseInputStream, mainCredential.masterPassword, keyFileInputStream, @@ -483,7 +484,7 @@ class Database { fixDuplicateUUID) }, { databaseInputStream -> - DatabaseInputKDBX(cacheDirectory) + DatabaseInputKDBX(cacheDirectory, isRAMSufficient) .openDatabase(databaseInputStream, mainCredential.masterPassword, keyFileInputStream, @@ -507,6 +508,7 @@ class Database { @Throws(LoadDatabaseException::class) fun reloadData(contentResolver: ContentResolver, cacheDirectory: File, + isRAMSufficient: (memoryWanted: Long) -> Boolean, tempCipherKey: LoadedKey, progressTaskUpdater: ProgressTaskUpdater?) { @@ -515,14 +517,14 @@ class Database { fileUri?.let { oldDatabaseUri -> readDatabaseStream(contentResolver, oldDatabaseUri, { databaseInputStream -> - DatabaseInputKDB(cacheDirectory) + DatabaseInputKDB(cacheDirectory, isRAMSufficient) .openDatabase(databaseInputStream, masterKey, tempCipherKey, progressTaskUpdater) }, { databaseInputStream -> - DatabaseInputKDBX(cacheDirectory) + DatabaseInputKDBX(cacheDirectory, isRAMSufficient) .openDatabase(databaseInputStream, masterKey, tempCipherKey, @@ -576,8 +578,7 @@ class Database { val attachmentPool: AttachmentPool get() { - // Binary pool is functionally only in KDBX - return mDatabaseKDBX?.binaryPool ?: AttachmentPool() + return mDatabaseKDB?.binaryPool ?: mDatabaseKDBX?.binaryPool ?: AttachmentPool() } val allowMultipleAttachments: Boolean @@ -593,7 +594,7 @@ class Database { compressed: Boolean = false, protected: Boolean = false): BinaryData? { return mDatabaseKDB?.buildNewAttachment(cacheDirectory) - ?: mDatabaseKDBX?.buildNewAttachment(cacheDirectory, compressed, protected) + ?: mDatabaseKDBX?.buildNewAttachment(cacheDirectory, false, compressed, protected) } fun removeAttachmentIfNotUsed(attachment: Attachment) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt index 1d0eeee1a..1d9ae3395 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt @@ -311,7 +311,7 @@ class Entry : Node, EntryVersionedInterface { fun getAttachments(attachmentPool: AttachmentPool, inHistory: Boolean = false): List { val attachments = ArrayList() - entryKDB?.getAttachment()?.let { + entryKDB?.getAttachment(attachmentPool)?.let { attachments.add(it) } entryKDBX?.getAttachments(attachmentPool, inHistory)?.let { @@ -336,7 +336,7 @@ class Entry : Node, EntryVersionedInterface { } private fun putAttachment(attachment: Attachment, attachmentPool: AttachmentPool) { - entryKDB?.putAttachment(attachment) + entryKDB?.putAttachment(attachment, attachmentPool) entryKDBX?.putAttachment(attachment, attachmentPool) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryByte.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryByte.kt index f5677b3d9..a66472daf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryByte.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryByte.kt @@ -19,8 +19,6 @@ */ package com.kunzisoft.keepass.database.element.database -import android.os.Parcel -import android.os.Parcelable import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.stream.readAllBytes import java.io.* @@ -35,18 +33,15 @@ class BinaryByte : BinaryData { */ constructor() : super() + constructor(compressed: Boolean = false, + protected: Boolean = false) : super(compressed, protected) + constructor(byteArray: ByteArray, compressed: Boolean = false, protected: Boolean = false) : super(compressed, protected) { this.mDataByte = byteArray } - constructor(parcel: Parcel) : super(parcel) { - val byteArray = ByteArray(parcel.readInt()) - parcel.readByteArray(byteArray) - mDataByte = byteArray - } - @Throws(IOException::class) override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream { return ByteArrayInputStream(mDataByte) @@ -112,12 +107,6 @@ class BinaryByte : BinaryData { return mDataByte.toString() } - override fun writeToParcel(dest: Parcel, flags: Int) { - super.writeToParcel(dest, flags) - dest.writeInt(mDataByte.size) - dest.writeByteArray(mDataByte) - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is BinaryByte) return false @@ -145,21 +134,7 @@ class BinaryByte : BinaryData { } companion object { - private val TAG = BinaryByte::class.java.name - // Max Parcelable / 2 - const val MAX_BINARY_BYTES = 524288 - - @JvmField - val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): BinaryByte { - return BinaryByte(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt index c51493789..63321f3ec 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryData.kt @@ -19,8 +19,8 @@ */ package com.kunzisoft.keepass.database.element.database -import android.os.Parcel -import android.os.Parcelable +import android.app.ActivityManager +import android.content.Context import com.kunzisoft.keepass.database.element.Database import java.io.IOException import java.io.InputStream @@ -28,7 +28,7 @@ import java.io.OutputStream import java.util.zip.GZIPInputStream import java.util.zip.GZIPOutputStream -abstract class BinaryData : Parcelable { +abstract class BinaryData { var isCompressed: Boolean = false protected set @@ -46,12 +46,6 @@ abstract class BinaryData : Parcelable { this.isProtected = protected } - protected constructor(parcel: Parcel) { - isCompressed = parcel.readByte().toInt() != 0 - isProtected = parcel.readByte().toInt() != 0 - isCorrupted = parcel.readByte().toInt() != 0 - } - @Throws(IOException::class) abstract fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream @@ -91,16 +85,6 @@ abstract class BinaryData : Parcelable { abstract fun binaryHash(): Int - override fun describeContents(): Int { - return 0 - } - - 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()) - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is BinaryData) return false @@ -121,6 +105,13 @@ abstract class BinaryData : Parcelable { companion object { private val TAG = BinaryData::class.java.name + + fun canMemoryBeAllocatedInRAM(context: Context, memoryWanted: Long): Boolean { + val memoryInfo = ActivityManager.MemoryInfo() + (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(memoryInfo) + val availableMemory = memoryInfo.availMem + return availableMemory > memoryWanted * 3 + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt index f3cd61060..70e2347b2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt @@ -19,8 +19,6 @@ */ package com.kunzisoft.keepass.database.element.database -import android.os.Parcel -import android.os.Parcelable import android.util.Base64 import android.util.Base64InputStream import android.util.Base64OutputStream @@ -57,14 +55,6 @@ class BinaryFile : BinaryData { this.mBinaryHash = 0 } - constructor(parcel: Parcel) : super(parcel) { - parcel.readString()?.let { - mDataFile = File(it) - } - mLength = parcel.readLong() - mBinaryHash = parcel.readInt() - } - @Throws(IOException::class) override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream { return buildInputStream(mDataFile, cipherKey) @@ -172,13 +162,6 @@ class BinaryFile : BinaryData { return mDataFile.toString() } - override fun writeToParcel(dest: Parcel, flags: Int) { - super.writeToParcel(dest, flags) - dest.writeString(mDataFile?.absolutePath) - dest.writeLong(mLength) - dest.writeInt(mBinaryHash) - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is BinaryFile) return false @@ -236,19 +219,7 @@ class BinaryFile : BinaryData { } companion object { - private val TAG = BinaryFile::class.java.name - - @JvmField - val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): BinaryFile { - return BinaryFile(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt index 96c9555b0..00e24079c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt @@ -46,7 +46,7 @@ class DatabaseKDB : DatabaseVersioned() { private var kdfListV3: MutableList = ArrayList() // Only to generate unique file name - private var binaryPool = AttachmentPool() + var binaryPool = AttachmentPool() override val version: String get() = "KeePass 1" diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt index 5ebda8474..ca43b3ae9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt @@ -318,9 +318,9 @@ class DatabaseKDBX : DatabaseVersioned { fun addCustomIcon(cacheDirectory: File, customIconId: UUID? = null, - dataSize: Int, + smallSize: Boolean, result: (IconImageCustom, BinaryData?) -> Unit) { - iconsManager.addCustomIcon(cacheDirectory, customIconId, dataSize, result) + iconsManager.addCustomIcon(cacheDirectory, customIconId, smallSize, result) } fun isCustomIconBinaryDuplicate(binary: BinaryData): Boolean { @@ -642,12 +642,17 @@ class DatabaseKDBX : DatabaseVersioned { } fun buildNewAttachment(cacheDirectory: File, + smallSize: Boolean, compression: Boolean, protection: Boolean, binaryPoolId: Int? = null): BinaryData { return binaryPool.put(binaryPoolId) { uniqueBinaryId -> - val fileInCache = File(cacheDirectory, uniqueBinaryId) - BinaryFile(fileInCache, compression, protection) + if (smallSize) { + BinaryByte(compression, protection) + } else { + val fileInCache = File(cacheDirectory, uniqueBinaryId) + BinaryFile(fileInCache, compression, protection) + } }.binary } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt index 1b2acdee3..2ca3a2632 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt @@ -22,6 +22,7 @@ package com.kunzisoft.keepass.database.element.entry import android.os.Parcel import android.os.Parcelable import com.kunzisoft.keepass.database.element.Attachment +import com.kunzisoft.keepass.database.element.database.AttachmentPool import com.kunzisoft.keepass.database.element.database.BinaryData import com.kunzisoft.keepass.database.element.group.GroupKDB import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID @@ -56,7 +57,7 @@ class EntryKDB : EntryVersioned, NodeKDBInterface /** A string describing what is in binaryData */ var binaryDescription = "" - var binaryData: BinaryData? = null + private var binaryDataId: Int? = null // Determine if this is a MetaStream entry val isMetaStream: Boolean @@ -89,7 +90,7 @@ class EntryKDB : EntryVersioned, NodeKDBInterface url = parcel.readString() ?: url notes = parcel.readString() ?: notes binaryDescription = parcel.readString() ?: binaryDescription - binaryData = parcel.readParcelable(BinaryData::class.java.classLoader) + binaryDataId = parcel.readInt() } override fun readParentParcelable(parcel: Parcel): GroupKDB? { @@ -108,7 +109,9 @@ class EntryKDB : EntryVersioned, NodeKDBInterface dest.writeString(url) dest.writeString(notes) dest.writeString(binaryDescription) - dest.writeParcelable(binaryData, flags) + binaryDataId?.let { + dest.writeInt(it) + } } fun updateWith(source: EntryKDB) { @@ -119,7 +122,7 @@ class EntryKDB : EntryVersioned, NodeKDBInterface url = source.url notes = source.notes binaryDescription = source.binaryDescription - binaryData = source.binaryData + binaryDataId = source.binaryDataId } override var username = "" @@ -138,26 +141,39 @@ class EntryKDB : EntryVersioned, NodeKDBInterface override val type: Type get() = Type.ENTRY - fun getAttachment(): Attachment? { - val binary = binaryData - return if (binary != null) - Attachment(binaryDescription, binary) - else null + fun getAttachment(attachmentPool: AttachmentPool): Attachment? { + binaryDataId?.let { poolId -> + attachmentPool[poolId]?.let { binary -> + return Attachment(binaryDescription, binary) + } + } + return null } fun containsAttachment(): Boolean { - return binaryData != null + return binaryDataId != null } - fun putAttachment(attachment: Attachment) { + fun getBinary(attachmentPool: AttachmentPool): BinaryData? { + this.binaryDataId?.let { + return attachmentPool[it] + } + return null + } + + fun putBinary(binaryData: BinaryData, attachmentPool: AttachmentPool) { + this.binaryDataId = attachmentPool.put(binaryData) + } + + fun putAttachment(attachment: Attachment, attachmentPool: AttachmentPool) { this.binaryDescription = attachment.name - this.binaryData = attachment.binaryData + this.binaryDataId = attachmentPool.put(attachment.binaryData) } fun removeAttachment(attachment: Attachment? = null) { if (attachment == null || this.binaryDescription == attachment.name) { this.binaryDescription = "" - this.binaryData = null + this.binaryDataId = null } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconsManager.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconsManager.kt index d4b9a06e9..1d34a854c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconsManager.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconsManager.kt @@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.element.icon import android.util.Log import com.kunzisoft.keepass.database.element.database.BinaryByte -import com.kunzisoft.keepass.database.element.database.BinaryByte.Companion.MAX_BINARY_BYTES import com.kunzisoft.keepass.database.element.database.BinaryData import com.kunzisoft.keepass.database.element.database.BinaryFile import com.kunzisoft.keepass.database.element.database.CustomIconPool @@ -56,16 +55,16 @@ class IconsManager { key: UUID? = null, result: (IconImageCustom, BinaryData?) -> Unit) { // Create a binary file for a brand new custom icon - addCustomIcon(cacheDirectory, key, -1, result) + addCustomIcon(cacheDirectory, key, false, result) } fun addCustomIcon(cacheDirectory: File, key: UUID? = null, - dataSize: Int, + smallSize: Boolean, result: (IconImageCustom, BinaryData?) -> Unit) { val keyBinary = customCache.put(key) { uniqueBinaryId -> // Create a byte array for better performance with small data - if (dataSize in 1..MAX_BINARY_BYTES) { + if (smallSize) { BinaryByte() } else { val fileInCache = File(cacheDirectory, uniqueBinaryId) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt index 147b7ce74..9c85112f5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt @@ -26,8 +26,9 @@ import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import java.io.File import java.io.InputStream -abstract class DatabaseInput> - (protected val cacheDirectory: File) { +abstract class DatabaseInput> + (protected val cacheDirectory: File, + protected val isRAMSufficient: (memoryWanted: Long) -> Boolean) { /** * Load a versioned database file, return contents in a new DatabaseVersioned. @@ -45,7 +46,7 @@ abstract class DatabaseInput> keyfileInputStream: InputStream?, loadedCipherKey: Database.LoadedKey, progressTaskUpdater: ProgressTaskUpdater?, - fixDuplicateUUID: Boolean = false): PwDb + fixDuplicateUUID: Boolean = false): D @Throws(LoadDatabaseException::class) @@ -53,5 +54,5 @@ abstract class DatabaseInput> masterKey: ByteArray, loadedCipherKey: Database.LoadedKey, progressTaskUpdater: ProgressTaskUpdater?, - fixDuplicateUUID: Boolean = false): PwDb + fixDuplicateUUID: Boolean = false): D } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt index b41ef0949..b6602089a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt @@ -46,8 +46,9 @@ import javax.crypto.spec.SecretKeySpec /** * Load a KDB database file. */ -class DatabaseInputKDB(cacheDirectory: File) - : DatabaseInput(cacheDirectory) { +class DatabaseInputKDB(cacheDirectory: File, + isRAMSufficient: (memoryWanted: Long) -> Boolean) + : DatabaseInput(cacheDirectory, isRAMSufficient) { private lateinit var mDatabase: DatabaseKDB @@ -306,11 +307,11 @@ class DatabaseInputKDB(cacheDirectory: File) 0x000E -> { newEntry?.let { entry -> if (fieldSize > 0) { - val binaryAttachment = mDatabase.buildNewAttachment(cacheDirectory) - entry.binaryData = binaryAttachment + val binaryData = mDatabase.buildNewAttachment(cacheDirectory) + entry.putBinary(binaryData, mDatabase.binaryPool) val cipherKey = mDatabase.loadedCipherKey ?: throw IOException("Unable to retrieve cipher key to load binaries") - BufferedOutputStream(binaryAttachment.getOutputDataStream(cipherKey)).use { outputStream -> + BufferedOutputStream(binaryData.getOutputDataStream(cipherKey)).use { outputStream -> cipherInputStream.readBytes(fieldSize) { buffer -> outputStream.write(buffer) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt index e446ba5a0..a18caae07 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt @@ -63,8 +63,9 @@ import javax.crypto.Cipher import javax.crypto.CipherInputStream import kotlin.math.min -class DatabaseInputKDBX(cacheDirectory: File) - : DatabaseInput(cacheDirectory) { +class DatabaseInputKDBX(cacheDirectory: File, + isRAMSufficient: (memoryWanted: Long) -> Boolean) + : DatabaseInput(cacheDirectory, isRAMSufficient) { private var randomStream: StreamCipher? = null private lateinit var mDatabase: DatabaseKDBX @@ -276,7 +277,8 @@ class DatabaseInputKDBX(cacheDirectory: File) val protectedFlag = dataInputStream.read().toByte() == DatabaseHeaderKDBX.KdbxBinaryFlags.Protected val byteLength = size - 1 // No compression at this level - val protectedBinary = mDatabase.buildNewAttachment(cacheDirectory, false, protectedFlag) + val protectedBinary = mDatabase.buildNewAttachment(cacheDirectory, + isRAMSufficient.invoke(byteLength.toLong()), false, protectedFlag) val cipherKey = mDatabase.loadedCipherKey ?: throw IOException("Unable to retrieve cipher key to load binaries") protectedBinary.getOutputDataStream(cipherKey).use { outputStream -> @@ -703,11 +705,12 @@ class DatabaseInputKDBX(cacheDirectory: File) } else if (ctx == KdbContext.CustomIcons && name.equals(DatabaseKDBXXML.ElemCustomIcons, ignoreCase = true)) { return KdbContext.Meta } else if (ctx == KdbContext.CustomIcon && name.equals(DatabaseKDBXXML.ElemCustomIconItem, ignoreCase = true)) { - if (customIconID != DatabaseVersioned.UUID_ZERO && customIconData != null) { - mDatabase.addCustomIcon(cacheDirectory, customIconID, customIconData!!.size) { _, binary -> + val iconData = customIconData + if (customIconID != DatabaseVersioned.UUID_ZERO && iconData != null) { + mDatabase.addCustomIcon(cacheDirectory, customIconID, isRAMSufficient.invoke(iconData.size.toLong())) { _, binary -> mDatabase.loadedCipherKey?.let { cipherKey -> binary?.getOutputDataStream(cipherKey)?.use { outputStream -> - outputStream.write(customIconData) + outputStream.write(iconData) } } } @@ -981,7 +984,7 @@ class DatabaseInputKDBX(cacheDirectory: File) var binaryRetrieve = mDatabase.binaryPool[id] // Create empty binary if not retrieved in pool if (binaryRetrieve == null) { - binaryRetrieve = mDatabase.buildNewAttachment(cacheDirectory, + binaryRetrieve = mDatabase.buildNewAttachment(cacheDirectory, false, compression = false, protection = false, binaryPoolId = id) } return binaryRetrieve @@ -1018,7 +1021,8 @@ class DatabaseInputKDBX(cacheDirectory: File) return null // Build the new binary and compress - val binaryAttachment = mDatabase.buildNewAttachment(cacheDirectory, compressed, protected, binaryId) + val binaryAttachment = mDatabase.buildNewAttachment(cacheDirectory, + isRAMSufficient.invoke(base64.length.toLong()), compressed, protected, binaryId) val binaryCipherKey = mDatabase.loadedCipherKey ?: throw IOException("Unable to retrieve cipher key to load binaries") try { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt index a59752eb4..16d704560 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt @@ -217,7 +217,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, } // Entries mDatabaseKDB.doForEachEntryInIndex { entry -> - EntryOutputKDB(entry, outputStream, mDatabaseKDB.loadedCipherKey).output() + EntryOutputKDB(entry, outputStream, mDatabaseKDB.loadedCipherKey).output(mDatabaseKDB) } } 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 a18aa8201..e605c48fe 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 @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.file.output import android.util.Log import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.database.DatabaseKDB import com.kunzisoft.keepass.database.element.entry.EntryKDB import com.kunzisoft.keepass.database.exception.DatabaseOutputException import com.kunzisoft.keepass.stream.* @@ -39,7 +40,7 @@ class EntryOutputKDB(private val mEntry: EntryKDB, //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int @Throws(DatabaseOutputException::class) - fun output() { + fun output(database: DatabaseKDB) { try { // UUID mOutputStream.write(UUID_FIELD_TYPE) @@ -96,7 +97,7 @@ class EntryOutputKDB(private val mEntry: EntryKDB, // Binary mCipherKey?.let { cipherKey -> mOutputStream.write(BINARY_DATA_FIELD_TYPE) - val binaryData = mEntry.binaryData + val binaryData = mEntry.getBinary(database.binaryPool) val binaryDataLength = binaryData?.getSize() ?: 0L // Write data length mOutputStream.write(uIntTo4Bytes(UnsignedInt.fromKotlinLong(binaryDataLength)))