First commit to allocate dynamic memory

This commit is contained in:
J-Jamet
2021-03-23 13:07:49 +01:00
parent 492382d552
commit 520c6b60be
17 changed files with 105 additions and 129 deletions

View File

@@ -25,6 +25,7 @@ import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database 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.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.model.MainCredential import com.kunzisoft.keepass.model.MainCredential
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
@@ -55,6 +56,9 @@ class LoadDatabaseRunnable(private val context: Context,
mReadonly, mReadonly,
context.contentResolver, context.contentResolver,
UriUtil.getBinaryDir(context), UriUtil.getBinaryDir(context),
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
Database.LoadedKey.generateNewCipherKey(), Database.LoadedKey.generateNewCipherKey(),
mFixDuplicateUUID, mFixDuplicateUUID,
progressTaskUpdater) progressTaskUpdater)

View File

@@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.action
import android.content.Context import android.content.Context
import com.kunzisoft.keepass.database.element.Database 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.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
@@ -46,6 +47,9 @@ class ReloadDatabaseRunnable(private val context: Context,
try { try {
mDatabase.reloadData(context.contentResolver, mDatabase.reloadData(context.contentResolver,
UriUtil.getBinaryDir(context), UriUtil.getBinaryDir(context),
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
tempCipherKey ?: Database.LoadedKey.generateNewCipherKey(), tempCipherKey ?: Database.LoadedKey.generateNewCipherKey(),
progressTaskUpdater) progressTaskUpdater)
} catch (e: LoadDatabaseException) { } catch (e: LoadDatabaseException) {

View File

@@ -30,12 +30,15 @@ data class Attachment(var name: String,
constructor(parcel: Parcel) : this( constructor(parcel: Parcel) : this(
parcel.readString() ?: "", 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) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name) parcel.writeString(name)
parcel.writeParcelable(binaryData, flags) // TODO BinaryParcelable
//parcel.writeParcelable(binaryData, flags)
} }
override fun describeContents(): Int { override fun describeContents(): Int {

View File

@@ -453,6 +453,7 @@ class Database {
readOnly: Boolean, readOnly: Boolean,
contentResolver: ContentResolver, contentResolver: ContentResolver,
cacheDirectory: File, cacheDirectory: File,
isRAMSufficient: (memoryWanted: Long) -> Boolean,
tempCipherKey: LoadedKey, tempCipherKey: LoadedKey,
fixDuplicateUUID: Boolean, fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?) { progressTaskUpdater: ProgressTaskUpdater?) {
@@ -474,7 +475,7 @@ class Database {
// Read database stream for the first time // Read database stream for the first time
readDatabaseStream(contentResolver, uri, readDatabaseStream(contentResolver, uri,
{ databaseInputStream -> { databaseInputStream ->
DatabaseInputKDB(cacheDirectory) DatabaseInputKDB(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream, .openDatabase(databaseInputStream,
mainCredential.masterPassword, mainCredential.masterPassword,
keyFileInputStream, keyFileInputStream,
@@ -483,7 +484,7 @@ class Database {
fixDuplicateUUID) fixDuplicateUUID)
}, },
{ databaseInputStream -> { databaseInputStream ->
DatabaseInputKDBX(cacheDirectory) DatabaseInputKDBX(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream, .openDatabase(databaseInputStream,
mainCredential.masterPassword, mainCredential.masterPassword,
keyFileInputStream, keyFileInputStream,
@@ -507,6 +508,7 @@ class Database {
@Throws(LoadDatabaseException::class) @Throws(LoadDatabaseException::class)
fun reloadData(contentResolver: ContentResolver, fun reloadData(contentResolver: ContentResolver,
cacheDirectory: File, cacheDirectory: File,
isRAMSufficient: (memoryWanted: Long) -> Boolean,
tempCipherKey: LoadedKey, tempCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?) { progressTaskUpdater: ProgressTaskUpdater?) {
@@ -515,14 +517,14 @@ class Database {
fileUri?.let { oldDatabaseUri -> fileUri?.let { oldDatabaseUri ->
readDatabaseStream(contentResolver, oldDatabaseUri, readDatabaseStream(contentResolver, oldDatabaseUri,
{ databaseInputStream -> { databaseInputStream ->
DatabaseInputKDB(cacheDirectory) DatabaseInputKDB(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream, .openDatabase(databaseInputStream,
masterKey, masterKey,
tempCipherKey, tempCipherKey,
progressTaskUpdater) progressTaskUpdater)
}, },
{ databaseInputStream -> { databaseInputStream ->
DatabaseInputKDBX(cacheDirectory) DatabaseInputKDBX(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream, .openDatabase(databaseInputStream,
masterKey, masterKey,
tempCipherKey, tempCipherKey,
@@ -576,8 +578,7 @@ class Database {
val attachmentPool: AttachmentPool val attachmentPool: AttachmentPool
get() { get() {
// Binary pool is functionally only in KDBX return mDatabaseKDB?.binaryPool ?: mDatabaseKDBX?.binaryPool ?: AttachmentPool()
return mDatabaseKDBX?.binaryPool ?: AttachmentPool()
} }
val allowMultipleAttachments: Boolean val allowMultipleAttachments: Boolean
@@ -593,7 +594,7 @@ class Database {
compressed: Boolean = false, compressed: Boolean = false,
protected: Boolean = false): BinaryData? { protected: Boolean = false): BinaryData? {
return mDatabaseKDB?.buildNewAttachment(cacheDirectory) return mDatabaseKDB?.buildNewAttachment(cacheDirectory)
?: mDatabaseKDBX?.buildNewAttachment(cacheDirectory, compressed, protected) ?: mDatabaseKDBX?.buildNewAttachment(cacheDirectory, false, compressed, protected)
} }
fun removeAttachmentIfNotUsed(attachment: Attachment) { fun removeAttachmentIfNotUsed(attachment: Attachment) {

View File

@@ -311,7 +311,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
fun getAttachments(attachmentPool: AttachmentPool, inHistory: Boolean = false): List<Attachment> { fun getAttachments(attachmentPool: AttachmentPool, inHistory: Boolean = false): List<Attachment> {
val attachments = ArrayList<Attachment>() val attachments = ArrayList<Attachment>()
entryKDB?.getAttachment()?.let { entryKDB?.getAttachment(attachmentPool)?.let {
attachments.add(it) attachments.add(it)
} }
entryKDBX?.getAttachments(attachmentPool, inHistory)?.let { entryKDBX?.getAttachments(attachmentPool, inHistory)?.let {
@@ -336,7 +336,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
} }
private fun putAttachment(attachment: Attachment, attachmentPool: AttachmentPool) { private fun putAttachment(attachment: Attachment, attachmentPool: AttachmentPool) {
entryKDB?.putAttachment(attachment) entryKDB?.putAttachment(attachment, attachmentPool)
entryKDBX?.putAttachment(attachment, attachmentPool) entryKDBX?.putAttachment(attachment, attachmentPool)
} }

View File

@@ -19,8 +19,6 @@
*/ */
package com.kunzisoft.keepass.database.element.database 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.database.element.Database
import com.kunzisoft.keepass.stream.readAllBytes import com.kunzisoft.keepass.stream.readAllBytes
import java.io.* import java.io.*
@@ -35,18 +33,15 @@ class BinaryByte : BinaryData {
*/ */
constructor() : super() constructor() : super()
constructor(compressed: Boolean = false,
protected: Boolean = false) : super(compressed, protected)
constructor(byteArray: ByteArray, constructor(byteArray: ByteArray,
compressed: Boolean = false, compressed: Boolean = false,
protected: Boolean = false) : super(compressed, protected) { protected: Boolean = false) : super(compressed, protected) {
this.mDataByte = byteArray this.mDataByte = byteArray
} }
constructor(parcel: Parcel) : super(parcel) {
val byteArray = ByteArray(parcel.readInt())
parcel.readByteArray(byteArray)
mDataByte = byteArray
}
@Throws(IOException::class) @Throws(IOException::class)
override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream { override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream {
return ByteArrayInputStream(mDataByte) return ByteArrayInputStream(mDataByte)
@@ -112,12 +107,6 @@ class BinaryByte : BinaryData {
return mDataByte.toString() 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 { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is BinaryByte) return false if (other !is BinaryByte) return false
@@ -145,21 +134,7 @@ class BinaryByte : BinaryData {
} }
companion object { companion object {
private val TAG = BinaryByte::class.java.name private val TAG = BinaryByte::class.java.name
// Max Parcelable / 2
const val MAX_BINARY_BYTES = 524288
@JvmField
val CREATOR: Parcelable.Creator<BinaryByte> = object : Parcelable.Creator<BinaryByte> {
override fun createFromParcel(parcel: Parcel): BinaryByte {
return BinaryByte(parcel)
}
override fun newArray(size: Int): Array<BinaryByte?> {
return arrayOfNulls(size)
}
}
} }
} }

View File

@@ -19,8 +19,8 @@
*/ */
package com.kunzisoft.keepass.database.element.database package com.kunzisoft.keepass.database.element.database
import android.os.Parcel import android.app.ActivityManager
import android.os.Parcelable import android.content.Context
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
@@ -28,7 +28,7 @@ import java.io.OutputStream
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
abstract class BinaryData : Parcelable { abstract class BinaryData {
var isCompressed: Boolean = false var isCompressed: Boolean = false
protected set protected set
@@ -46,12 +46,6 @@ abstract class BinaryData : Parcelable {
this.isProtected = protected 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) @Throws(IOException::class)
abstract fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream abstract fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream
@@ -91,16 +85,6 @@ abstract class BinaryData : Parcelable {
abstract fun binaryHash(): Int 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 { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is BinaryData) return false if (other !is BinaryData) return false
@@ -121,6 +105,13 @@ abstract class BinaryData : Parcelable {
companion object { companion object {
private val TAG = BinaryData::class.java.name 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
}
} }
} }

View File

@@ -19,8 +19,6 @@
*/ */
package com.kunzisoft.keepass.database.element.database package com.kunzisoft.keepass.database.element.database
import android.os.Parcel
import android.os.Parcelable
import android.util.Base64 import android.util.Base64
import android.util.Base64InputStream import android.util.Base64InputStream
import android.util.Base64OutputStream import android.util.Base64OutputStream
@@ -57,14 +55,6 @@ class BinaryFile : BinaryData {
this.mBinaryHash = 0 this.mBinaryHash = 0
} }
constructor(parcel: Parcel) : super(parcel) {
parcel.readString()?.let {
mDataFile = File(it)
}
mLength = parcel.readLong()
mBinaryHash = parcel.readInt()
}
@Throws(IOException::class) @Throws(IOException::class)
override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream { override fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream {
return buildInputStream(mDataFile, cipherKey) return buildInputStream(mDataFile, cipherKey)
@@ -172,13 +162,6 @@ class BinaryFile : BinaryData {
return mDataFile.toString() 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 { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is BinaryFile) return false if (other !is BinaryFile) return false
@@ -236,19 +219,7 @@ class BinaryFile : BinaryData {
} }
companion object { companion object {
private val TAG = BinaryFile::class.java.name private val TAG = BinaryFile::class.java.name
@JvmField
val CREATOR: Parcelable.Creator<BinaryFile> = object : Parcelable.Creator<BinaryFile> {
override fun createFromParcel(parcel: Parcel): BinaryFile {
return BinaryFile(parcel)
}
override fun newArray(size: Int): Array<BinaryFile?> {
return arrayOfNulls(size)
}
}
} }
} }

View File

@@ -46,7 +46,7 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
private var kdfListV3: MutableList<KdfEngine> = ArrayList() private var kdfListV3: MutableList<KdfEngine> = ArrayList()
// Only to generate unique file name // Only to generate unique file name
private var binaryPool = AttachmentPool() var binaryPool = AttachmentPool()
override val version: String override val version: String
get() = "KeePass 1" get() = "KeePass 1"

View File

@@ -318,9 +318,9 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
fun addCustomIcon(cacheDirectory: File, fun addCustomIcon(cacheDirectory: File,
customIconId: UUID? = null, customIconId: UUID? = null,
dataSize: Int, smallSize: Boolean,
result: (IconImageCustom, BinaryData?) -> Unit) { result: (IconImageCustom, BinaryData?) -> Unit) {
iconsManager.addCustomIcon(cacheDirectory, customIconId, dataSize, result) iconsManager.addCustomIcon(cacheDirectory, customIconId, smallSize, result)
} }
fun isCustomIconBinaryDuplicate(binary: BinaryData): Boolean { fun isCustomIconBinaryDuplicate(binary: BinaryData): Boolean {
@@ -642,12 +642,17 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
} }
fun buildNewAttachment(cacheDirectory: File, fun buildNewAttachment(cacheDirectory: File,
smallSize: Boolean,
compression: Boolean, compression: Boolean,
protection: Boolean, protection: Boolean,
binaryPoolId: Int? = null): BinaryData { binaryPoolId: Int? = null): BinaryData {
return binaryPool.put(binaryPoolId) { uniqueBinaryId -> return binaryPool.put(binaryPoolId) { uniqueBinaryId ->
val fileInCache = File(cacheDirectory, uniqueBinaryId) if (smallSize) {
BinaryFile(fileInCache, compression, protection) BinaryByte(compression, protection)
} else {
val fileInCache = File(cacheDirectory, uniqueBinaryId)
BinaryFile(fileInCache, compression, protection)
}
}.binary }.binary
} }

View File

@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.database.element.entry
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import com.kunzisoft.keepass.database.element.Attachment 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.database.BinaryData
import com.kunzisoft.keepass.database.element.group.GroupKDB import com.kunzisoft.keepass.database.element.group.GroupKDB
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID
@@ -56,7 +57,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
/** A string describing what is in binaryData */ /** A string describing what is in binaryData */
var binaryDescription = "" var binaryDescription = ""
var binaryData: BinaryData? = null private var binaryDataId: Int? = null
// Determine if this is a MetaStream entry // Determine if this is a MetaStream entry
val isMetaStream: Boolean val isMetaStream: Boolean
@@ -89,7 +90,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
url = parcel.readString() ?: url url = parcel.readString() ?: url
notes = parcel.readString() ?: notes notes = parcel.readString() ?: notes
binaryDescription = parcel.readString() ?: binaryDescription binaryDescription = parcel.readString() ?: binaryDescription
binaryData = parcel.readParcelable(BinaryData::class.java.classLoader) binaryDataId = parcel.readInt()
} }
override fun readParentParcelable(parcel: Parcel): GroupKDB? { override fun readParentParcelable(parcel: Parcel): GroupKDB? {
@@ -108,7 +109,9 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
dest.writeString(url) dest.writeString(url)
dest.writeString(notes) dest.writeString(notes)
dest.writeString(binaryDescription) dest.writeString(binaryDescription)
dest.writeParcelable(binaryData, flags) binaryDataId?.let {
dest.writeInt(it)
}
} }
fun updateWith(source: EntryKDB) { fun updateWith(source: EntryKDB) {
@@ -119,7 +122,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
url = source.url url = source.url
notes = source.notes notes = source.notes
binaryDescription = source.binaryDescription binaryDescription = source.binaryDescription
binaryData = source.binaryData binaryDataId = source.binaryDataId
} }
override var username = "" override var username = ""
@@ -138,26 +141,39 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
override val type: Type override val type: Type
get() = Type.ENTRY get() = Type.ENTRY
fun getAttachment(): Attachment? { fun getAttachment(attachmentPool: AttachmentPool): Attachment? {
val binary = binaryData binaryDataId?.let { poolId ->
return if (binary != null) attachmentPool[poolId]?.let { binary ->
Attachment(binaryDescription, binary) return Attachment(binaryDescription, binary)
else null }
}
return null
} }
fun containsAttachment(): Boolean { 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.binaryDescription = attachment.name
this.binaryData = attachment.binaryData this.binaryDataId = attachmentPool.put(attachment.binaryData)
} }
fun removeAttachment(attachment: Attachment? = null) { fun removeAttachment(attachment: Attachment? = null) {
if (attachment == null || this.binaryDescription == attachment.name) { if (attachment == null || this.binaryDescription == attachment.name) {
this.binaryDescription = "" this.binaryDescription = ""
this.binaryData = null this.binaryDataId = null
} }
} }

View File

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.element.icon
import android.util.Log import android.util.Log
import com.kunzisoft.keepass.database.element.database.BinaryByte 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.BinaryData
import com.kunzisoft.keepass.database.element.database.BinaryFile import com.kunzisoft.keepass.database.element.database.BinaryFile
import com.kunzisoft.keepass.database.element.database.CustomIconPool import com.kunzisoft.keepass.database.element.database.CustomIconPool
@@ -56,16 +55,16 @@ class IconsManager {
key: UUID? = null, key: UUID? = null,
result: (IconImageCustom, BinaryData?) -> Unit) { result: (IconImageCustom, BinaryData?) -> Unit) {
// Create a binary file for a brand new custom icon // Create a binary file for a brand new custom icon
addCustomIcon(cacheDirectory, key, -1, result) addCustomIcon(cacheDirectory, key, false, result)
} }
fun addCustomIcon(cacheDirectory: File, fun addCustomIcon(cacheDirectory: File,
key: UUID? = null, key: UUID? = null,
dataSize: Int, smallSize: Boolean,
result: (IconImageCustom, BinaryData?) -> Unit) { result: (IconImageCustom, BinaryData?) -> Unit) {
val keyBinary = customCache.put(key) { uniqueBinaryId -> val keyBinary = customCache.put(key) { uniqueBinaryId ->
// Create a byte array for better performance with small data // Create a byte array for better performance with small data
if (dataSize in 1..MAX_BINARY_BYTES) { if (smallSize) {
BinaryByte() BinaryByte()
} else { } else {
val fileInCache = File(cacheDirectory, uniqueBinaryId) val fileInCache = File(cacheDirectory, uniqueBinaryId)

View File

@@ -26,8 +26,9 @@ import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
abstract class DatabaseInput<PwDb : DatabaseVersioned<*, *, *, *>> abstract class DatabaseInput<D : DatabaseVersioned<*, *, *, *>>
(protected val cacheDirectory: File) { (protected val cacheDirectory: File,
protected val isRAMSufficient: (memoryWanted: Long) -> Boolean) {
/** /**
* Load a versioned database file, return contents in a new DatabaseVersioned. * Load a versioned database file, return contents in a new DatabaseVersioned.
@@ -45,7 +46,7 @@ abstract class DatabaseInput<PwDb : DatabaseVersioned<*, *, *, *>>
keyfileInputStream: InputStream?, keyfileInputStream: InputStream?,
loadedCipherKey: Database.LoadedKey, loadedCipherKey: Database.LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?, progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean = false): PwDb fixDuplicateUUID: Boolean = false): D
@Throws(LoadDatabaseException::class) @Throws(LoadDatabaseException::class)
@@ -53,5 +54,5 @@ abstract class DatabaseInput<PwDb : DatabaseVersioned<*, *, *, *>>
masterKey: ByteArray, masterKey: ByteArray,
loadedCipherKey: Database.LoadedKey, loadedCipherKey: Database.LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?, progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean = false): PwDb fixDuplicateUUID: Boolean = false): D
} }

View File

@@ -46,8 +46,9 @@ import javax.crypto.spec.SecretKeySpec
/** /**
* Load a KDB database file. * Load a KDB database file.
*/ */
class DatabaseInputKDB(cacheDirectory: File) class DatabaseInputKDB(cacheDirectory: File,
: DatabaseInput<DatabaseKDB>(cacheDirectory) { isRAMSufficient: (memoryWanted: Long) -> Boolean)
: DatabaseInput<DatabaseKDB>(cacheDirectory, isRAMSufficient) {
private lateinit var mDatabase: DatabaseKDB private lateinit var mDatabase: DatabaseKDB
@@ -306,11 +307,11 @@ class DatabaseInputKDB(cacheDirectory: File)
0x000E -> { 0x000E -> {
newEntry?.let { entry -> newEntry?.let { entry ->
if (fieldSize > 0) { if (fieldSize > 0) {
val binaryAttachment = mDatabase.buildNewAttachment(cacheDirectory) val binaryData = mDatabase.buildNewAttachment(cacheDirectory)
entry.binaryData = binaryAttachment entry.putBinary(binaryData, mDatabase.binaryPool)
val cipherKey = mDatabase.loadedCipherKey val cipherKey = mDatabase.loadedCipherKey
?: throw IOException("Unable to retrieve cipher key to load binaries") ?: 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 -> cipherInputStream.readBytes(fieldSize) { buffer ->
outputStream.write(buffer) outputStream.write(buffer)
} }

View File

@@ -63,8 +63,9 @@ import javax.crypto.Cipher
import javax.crypto.CipherInputStream import javax.crypto.CipherInputStream
import kotlin.math.min import kotlin.math.min
class DatabaseInputKDBX(cacheDirectory: File) class DatabaseInputKDBX(cacheDirectory: File,
: DatabaseInput<DatabaseKDBX>(cacheDirectory) { isRAMSufficient: (memoryWanted: Long) -> Boolean)
: DatabaseInput<DatabaseKDBX>(cacheDirectory, isRAMSufficient) {
private var randomStream: StreamCipher? = null private var randomStream: StreamCipher? = null
private lateinit var mDatabase: DatabaseKDBX private lateinit var mDatabase: DatabaseKDBX
@@ -276,7 +277,8 @@ class DatabaseInputKDBX(cacheDirectory: File)
val protectedFlag = dataInputStream.read().toByte() == DatabaseHeaderKDBX.KdbxBinaryFlags.Protected val protectedFlag = dataInputStream.read().toByte() == DatabaseHeaderKDBX.KdbxBinaryFlags.Protected
val byteLength = size - 1 val byteLength = size - 1
// No compression at this level // 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 val cipherKey = mDatabase.loadedCipherKey
?: throw IOException("Unable to retrieve cipher key to load binaries") ?: throw IOException("Unable to retrieve cipher key to load binaries")
protectedBinary.getOutputDataStream(cipherKey).use { outputStream -> protectedBinary.getOutputDataStream(cipherKey).use { outputStream ->
@@ -703,11 +705,12 @@ class DatabaseInputKDBX(cacheDirectory: File)
} else if (ctx == KdbContext.CustomIcons && name.equals(DatabaseKDBXXML.ElemCustomIcons, ignoreCase = true)) { } else if (ctx == KdbContext.CustomIcons && name.equals(DatabaseKDBXXML.ElemCustomIcons, ignoreCase = true)) {
return KdbContext.Meta return KdbContext.Meta
} else if (ctx == KdbContext.CustomIcon && name.equals(DatabaseKDBXXML.ElemCustomIconItem, ignoreCase = true)) { } else if (ctx == KdbContext.CustomIcon && name.equals(DatabaseKDBXXML.ElemCustomIconItem, ignoreCase = true)) {
if (customIconID != DatabaseVersioned.UUID_ZERO && customIconData != null) { val iconData = customIconData
mDatabase.addCustomIcon(cacheDirectory, customIconID, customIconData!!.size) { _, binary -> if (customIconID != DatabaseVersioned.UUID_ZERO && iconData != null) {
mDatabase.addCustomIcon(cacheDirectory, customIconID, isRAMSufficient.invoke(iconData.size.toLong())) { _, binary ->
mDatabase.loadedCipherKey?.let { cipherKey -> mDatabase.loadedCipherKey?.let { cipherKey ->
binary?.getOutputDataStream(cipherKey)?.use { outputStream -> binary?.getOutputDataStream(cipherKey)?.use { outputStream ->
outputStream.write(customIconData) outputStream.write(iconData)
} }
} }
} }
@@ -981,7 +984,7 @@ class DatabaseInputKDBX(cacheDirectory: File)
var binaryRetrieve = mDatabase.binaryPool[id] var binaryRetrieve = mDatabase.binaryPool[id]
// Create empty binary if not retrieved in pool // Create empty binary if not retrieved in pool
if (binaryRetrieve == null) { if (binaryRetrieve == null) {
binaryRetrieve = mDatabase.buildNewAttachment(cacheDirectory, binaryRetrieve = mDatabase.buildNewAttachment(cacheDirectory, false,
compression = false, protection = false, binaryPoolId = id) compression = false, protection = false, binaryPoolId = id)
} }
return binaryRetrieve return binaryRetrieve
@@ -1018,7 +1021,8 @@ class DatabaseInputKDBX(cacheDirectory: File)
return null return null
// Build the new binary and compress // 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 val binaryCipherKey = mDatabase.loadedCipherKey
?: throw IOException("Unable to retrieve cipher key to load binaries") ?: throw IOException("Unable to retrieve cipher key to load binaries")
try { try {

View File

@@ -217,7 +217,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
} }
// Entries // Entries
mDatabaseKDB.doForEachEntryInIndex { entry -> mDatabaseKDB.doForEachEntryInIndex { entry ->
EntryOutputKDB(entry, outputStream, mDatabaseKDB.loadedCipherKey).output() EntryOutputKDB(entry, outputStream, mDatabaseKDB.loadedCipherKey).output(mDatabaseKDB)
} }
} }

View File

@@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.file.output
import android.util.Log import android.util.Log
import com.kunzisoft.keepass.database.element.Database 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.element.entry.EntryKDB
import com.kunzisoft.keepass.database.exception.DatabaseOutputException import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.stream.* 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 //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int
@Throws(DatabaseOutputException::class) @Throws(DatabaseOutputException::class)
fun output() { fun output(database: DatabaseKDB) {
try { try {
// UUID // UUID
mOutputStream.write(UUID_FIELD_TYPE) mOutputStream.write(UUID_FIELD_TYPE)
@@ -96,7 +97,7 @@ class EntryOutputKDB(private val mEntry: EntryKDB,
// Binary // Binary
mCipherKey?.let { cipherKey -> mCipherKey?.let { cipherKey ->
mOutputStream.write(BINARY_DATA_FIELD_TYPE) mOutputStream.write(BINARY_DATA_FIELD_TYPE)
val binaryData = mEntry.binaryData val binaryData = mEntry.getBinary(database.binaryPool)
val binaryDataLength = binaryData?.getSize() ?: 0L val binaryDataLength = binaryData?.getSize() ?: 0L
// Write data length // Write data length
mOutputStream.write(uIntTo4Bytes(UnsignedInt.fromKotlinLong(binaryDataLength))) mOutputStream.write(uIntTo4Bytes(UnsignedInt.fromKotlinLong(binaryDataLength)))