Fix #327 save to previous database version when conditions are required

This commit is contained in:
J-Jamet
2019-09-11 16:54:58 +02:00
parent 5bd9da9bb1
commit c5e2ca9907
9 changed files with 64 additions and 22 deletions

View File

@@ -296,6 +296,8 @@ abstract class PwDatabase<Group : PwGroup<*, Group, Entry>, Entry : PwEntry<Grou
abstract fun rootCanContainsEntry(): Boolean
abstract fun containsCustomData(): Boolean
fun addGroupTo(newGroup: Group, parent: Group?) {
// Add tree to parent tree
parent?.addChildGroup(newGroup)

View File

@@ -154,6 +154,10 @@ class PwDatabaseV3 : PwDatabase<PwGroupV3, PwEntryV3>() {
return false
}
override fun containsCustomData(): Boolean {
return false
}
override fun isBackup(group: PwGroupV3): Boolean {
var currentGroup: PwGroupV3? = group
while (currentGroup != null) {

View File

@@ -196,6 +196,10 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
this.customData[label] = value
}
override fun containsCustomData(): Boolean {
return getCustomData().isNotEmpty()
}
@Throws(InvalidKeyFileException::class, IOException::class)
public override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {

View File

@@ -26,7 +26,7 @@ import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.MemoryUtil
import java.util.*
class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, PwNodeV4Interface {
// To decode each field not parcelable
@Transient
@@ -275,12 +275,12 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
return history.size
}
fun putCustomData(key: String, value: String) {
override fun putCustomData(key: String, value: String) {
customData[key] = value
}
fun containsCustomData(): Boolean {
return customData.size > 0
override fun containsCustomData(): Boolean {
return customData.isNotEmpty()
}
fun addEntryToHistory(entry: PwEntryV4) {

View File

@@ -25,7 +25,7 @@ import android.os.Parcelable
import java.util.HashMap
import java.util.UUID
class PwGroupV4 : PwGroup<UUID, PwGroupV4, PwEntryV4>, NodeV4Interface {
class PwGroupV4 : PwGroup<UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
// TODO Encapsulate
override var icon: PwIcon
@@ -123,12 +123,12 @@ class PwGroupV4 : PwGroup<UUID, PwGroupV4, PwEntryV4>, NodeV4Interface {
locationChanged = PwDate()
}
fun putCustomData(key: String, value: String) {
override fun putCustomData(key: String, value: String) {
customData[key] = value
}
fun containsCustomData(): Boolean {
return customData.size > 0
override fun containsCustomData(): Boolean {
return customData.isNotEmpty()
}
override fun allowAddEntryIfIsRoot(): Boolean {

View File

@@ -19,10 +19,14 @@
*/
package com.kunzisoft.keepass.database.element
interface NodeV4Interface : NodeTimeInterface {
interface PwNodeV4Interface : NodeTimeInterface {
var usageCount: Long
var locationChanged: PwDate
fun putCustomData(key: String, value: String)
fun containsCustomData(): Boolean
}

View File

@@ -23,7 +23,11 @@ import com.kunzisoft.keepass.crypto.CrsAlgorithm
import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters
import com.kunzisoft.keepass.database.NodeHandler
import com.kunzisoft.keepass.database.element.PwNodeV4Interface
import com.kunzisoft.keepass.database.element.PwDatabaseV4
import com.kunzisoft.keepass.database.element.PwEntryV4
import com.kunzisoft.keepass.database.element.PwGroupV4
import com.kunzisoft.keepass.database.exception.InvalidDBVersionException
import com.kunzisoft.keepass.stream.CopyInputStream
import com.kunzisoft.keepass.stream.HmacBlockStream
@@ -88,18 +92,42 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
this.masterSeed = ByteArray(32)
}
private inner class NodeHasCustomData<T:PwNodeV4Interface> : NodeHandler<T>() {
internal var containsCustomData = false
override fun operate(node: T): Boolean {
if (node.containsCustomData()) {
containsCustomData = true
return false
}
return true
}
}
private fun getMinKdbxVersion(databaseV4: PwDatabaseV4): Long {
// https://keepass.info/help/kb/kdbx_4.html
// Return v4 if AES is not use
if (databaseV4.kdfParameters != null
&& databaseV4.kdfParameters!!.uuid != AesKdf.CIPHER_UUID) {
return FILE_VERSION_32_4
}
if (databaseV4.rootGroup == null) {
return FILE_VERSION_32_3
}
// Return v4 if AES is not use
if (databaseV4.kdfParameters != null && databaseV4.kdfParameters!!.uuid != AesKdf.CIPHER_UUID) {
return FILE_VERSION_32_4
val entryHandler = NodeHasCustomData<PwEntryV4>()
val groupHandler = NodeHasCustomData<PwGroupV4>()
databaseV4.rootGroup?.doForEachChildAndForIt(entryHandler, groupHandler)
return if (databaseV4.containsCustomData()
|| entryHandler.containsCustomData
|| groupHandler.containsCustomData) {
FILE_VERSION_32_4
} else {
FILE_VERSION_32_3
}
// Return V4 by default
return FILE_VERSION_32_4
}
/** Assumes the input stream is at the beginning of the .kdbx file

View File

@@ -577,12 +577,12 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
}
KdbContext.GroupTimes, KdbContext.EntryTimes -> {
val tl: NodeV4Interface?
if (ctx == KdbContext.GroupTimes) {
tl = ctxGroup
} else {
tl = ctxEntry
}
val tl: PwNodeV4Interface? =
if (ctx == KdbContext.GroupTimes) {
ctxGroup
} else {
ctxEntry
}
when {
name.equals(PwDatabaseV4XML.ElemLastModTime, ignoreCase = true) -> tl?.lastModificationTime = readPwTime(xpp)

View File

@@ -653,7 +653,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
}
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun writeList(name: String?, it: NodeV4Interface) {
private fun writeList(name: String?, it: PwNodeV4Interface) {
assert(name != null)
xml.startTag(null, name)