diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt
index 092744fb8..57d5133c6 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt
@@ -21,9 +21,7 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.tasks.ActionRunnable
-import java.io.IOException
open class SaveDatabaseRunnable(protected var context: Context,
protected var database: Database,
@@ -38,9 +36,7 @@ open class SaveDatabaseRunnable(protected var context: Context,
if (saveDatabase && result.isSuccess) {
try {
database.saveData(context.contentResolver)
- } catch (e: IOException) {
- setError(e.message)
- } catch (e: DatabaseOutputException) {
+ } catch (e: Exception) {
setError(e.message)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/UpdateCompressionBinariesDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/UpdateCompressionBinariesDatabaseRunnable.kt
new file mode 100644
index 000000000..3ce9be00a
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/UpdateCompressionBinariesDatabaseRunnable.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeePass DX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.database.action
+
+import android.content.Context
+import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
+
+class UpdateCompressionBinariesDatabaseRunnable (
+ context: Context,
+ database: Database,
+ private val oldCompressionAlgorithm: PwCompressionAlgorithm,
+ private val newCompressionAlgorithm: PwCompressionAlgorithm,
+ saveDatabase: Boolean)
+ : SaveDatabaseRunnable(context, database, saveDatabase) {
+
+ override fun onStartRun() {
+ // Set new compression
+ if (database.allowDataCompression) {
+ try {
+ database.apply {
+ updateDataBinaryCompression(oldCompressionAlgorithm, newCompressionAlgorithm)
+ compressionAlgorithm = newCompressionAlgorithm
+ }
+ } catch (e: Exception) {
+ setError(e.message)
+ }
+ }
+
+ super.onStartRun()
+ }
+
+ override fun onFinishRun() {
+ super.onFinishRun()
+
+ if (database.allowDataCompression) {
+ if (!result.isSuccess) {
+ try {
+ database.apply {
+ compressionAlgorithm = oldCompressionAlgorithm
+ updateDataBinaryCompression(newCompressionAlgorithm, oldCompressionAlgorithm)
+ }
+ } catch (e: Exception) {
+ setError(e.message)
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt
index 66fb7620f..8622bcbc0 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt
@@ -20,25 +20,27 @@
package com.kunzisoft.keepass.database.element
import android.util.SparseArray
-import com.kunzisoft.keepass.database.element.security.ProtectedBinary
+import com.kunzisoft.keepass.database.element.security.BinaryAttachment
+import java.io.IOException
class BinaryPool {
- private val pool = SparseArray()
+ private val pool = SparseArray()
- operator fun get(key: Int): ProtectedBinary? {
+ operator fun get(key: Int): BinaryAttachment? {
return pool[key]
}
- fun put(key: Int, value: ProtectedBinary) {
+ fun put(key: Int, value: BinaryAttachment) {
pool.put(key, value)
}
- fun doForEachBinary(action: (key: Int, binary: ProtectedBinary) -> Unit) {
+ fun doForEachBinary(action: (key: Int, binary: BinaryAttachment) -> Unit) {
for (i in 0 until pool.size()) {
action.invoke(i, pool.get(pool.keyAt(i)))
}
}
+ @Throws(IOException::class)
fun clear() {
doForEachBinary { _, binary ->
binary.clear()
@@ -46,9 +48,9 @@ class BinaryPool {
pool.clear()
}
- fun add(protectedBinary: ProtectedBinary) {
- if (findKey(protectedBinary) == null) {
- pool.put(findUnusedKey(), protectedBinary)
+ fun add(fileBinary: BinaryAttachment) {
+ if (findKey(fileBinary) == null) {
+ pool.put(findUnusedKey(), fileBinary)
}
}
@@ -59,7 +61,7 @@ class BinaryPool {
return unusedKey
}
- fun findKey(pb: ProtectedBinary): Int? {
+ fun findKey(pb: BinaryAttachment): Int? {
for (i in 0 until pool.size()) {
if (pool.get(pool.keyAt(i)) == pb) return i
}
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 1f7131925..19a65bff2 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
@@ -139,6 +139,11 @@ class Database {
}
}
+ fun updateDataBinaryCompression(oldCompression: PwCompressionAlgorithm,
+ newCompression: PwCompressionAlgorithm) {
+ pwDatabaseV4?.changeBinaryCompression(oldCompression, newCompression)
+ }
+
val allowNoMasterKey: Boolean
get() = pwDatabaseV4 != null
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwCompressionAlgorithm.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwCompressionAlgorithm.kt
index c4bd02208..735bfb018 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwCompressionAlgorithm.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwCompressionAlgorithm.kt
@@ -20,17 +20,30 @@
package com.kunzisoft.keepass.database.element
import android.content.res.Resources
+import android.os.Parcel
+import android.os.Parcelable
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.ObjectNameResource
+import com.kunzisoft.keepass.utils.readEnum
+import com.kunzisoft.keepass.utils.writeEnum
+
// Note: We can get away with using int's to store unsigned 32-bit ints
// since we won't do arithmetic on these values (also unlikely to
// reach negative ids).
-enum class PwCompressionAlgorithm : ObjectNameResource {
+enum class PwCompressionAlgorithm : ObjectNameResource, Parcelable {
None,
GZip;
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ dest.writeEnum(this)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
override fun getName(resources: Resources): String {
return when (this) {
None -> resources.getString(R.string.compression_none)
@@ -38,4 +51,14 @@ enum class PwCompressionAlgorithm : ObjectNameResource {
}
}
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): PwCompressionAlgorithm {
+ return parcel.readEnum() ?: None
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt
index 8fad32a6c..99f48f596 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt
@@ -94,7 +94,7 @@ class PwDatabaseV4 : PwDatabase {
val customIcons = ArrayList()
val customData = HashMap()
- var binPool = BinaryPool()
+ var binaryPool = BinaryPool()
var localizedAppName = "KeePassDX"
@@ -161,6 +161,39 @@ class PwDatabaseV4 : PwDatabase {
return list
}
+ fun changeBinaryCompression(oldCompression: PwCompressionAlgorithm,
+ newCompression: PwCompressionAlgorithm) {
+ binaryPool.doForEachBinary { key, binary ->
+
+ try {
+ when (oldCompression) {
+ PwCompressionAlgorithm.None -> {
+ when (newCompression) {
+ PwCompressionAlgorithm.None -> {
+ }
+ PwCompressionAlgorithm.GZip -> {
+ // To compress, create a new binary with file
+ binary.compress()
+ }
+ }
+ }
+ PwCompressionAlgorithm.GZip -> {
+ when (newCompression) {
+ PwCompressionAlgorithm.None -> {
+ // To decompress, create a new binary with file
+ binary.decompress()
+ }
+ PwCompressionAlgorithm.GZip -> {
+ }
+ }
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to change compression for $key")
+ }
+ }
+ }
+
override val availableEncryptionAlgorithms: List
get() {
val list = ArrayList()
@@ -501,8 +534,12 @@ class PwDatabaseV4 : PwDatabase {
}
override fun clearCache() {
- super.clearCache()
- binPool.clear()
+ try {
+ super.clearCache()
+ binaryPool.clear()
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to clear cache", e)
+ }
}
companion object {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt
index cfb5510e5..5702e1d0b 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.element
import android.os.Parcel
import android.os.Parcelable
-import com.kunzisoft.keepass.database.element.security.ProtectedBinary
+import com.kunzisoft.keepass.database.element.security.BinaryAttachment
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.ParcelableUtil
import java.util.*
@@ -49,7 +49,7 @@ class PwEntryV4 : PwEntry, PwNodeV4Interface {
var iconCustom = PwIconCustom.UNKNOWN_ICON
private var customData = HashMap()
var fields = HashMap()
- var binaries = HashMap()
+ var binaries = HashMap()
var foregroundColor = ""
var backgroundColor = ""
var overrideURL = ""
@@ -98,7 +98,7 @@ class PwEntryV4 : PwEntry, PwNodeV4Interface {
locationChanged = parcel.readParcelable(PwDate::class.java.classLoader) ?: locationChanged
customData = ParcelableUtil.readStringParcelableMap(parcel)
fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
- binaries = ParcelableUtil.readStringParcelableMap(parcel, ProtectedBinary::class.java)
+ binaries = ParcelableUtil.readStringParcelableMap(parcel, BinaryAttachment::class.java)
foregroundColor = parcel.readString() ?: foregroundColor
backgroundColor = parcel.readString() ?: backgroundColor
overrideURL = parcel.readString() ?: overrideURL
@@ -274,7 +274,7 @@ class PwEntryV4 : PwEntry, PwNodeV4Interface {
fields[label] = value
}
- fun putProtectedBinary(key: String, value: ProtectedBinary) {
+ fun putProtectedBinary(key: String, value: BinaryAttachment) {
binaries[key] = value
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/security/BinaryAttachment.kt
similarity index 50%
rename from app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt
rename to app/src/main/java/com/kunzisoft/keepass/database/element/security/BinaryAttachment.kt
index 584a68400..29b552c3c 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/security/BinaryAttachment.kt
@@ -21,21 +21,22 @@ package com.kunzisoft.keepass.database.element.security
import android.os.Parcel
import android.os.Parcelable
-import android.util.Log
+import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BUFFER_SIZE_BYTES
+import com.kunzisoft.keepass.stream.ReadBytes
+import com.kunzisoft.keepass.stream.readFromStream
import java.io.*
+import java.util.zip.GZIPInputStream
+import java.util.zip.GZIPOutputStream
-import java.util.Arrays
+class BinaryAttachment : Parcelable {
-class ProtectedBinary : Parcelable {
-
- var isCompressed: Boolean? = null // Only for KDBX3.1-
+ var isCompressed: Boolean? = null
+ private set
var isProtected: Boolean = false
- private var data: ByteArray? = null
+ private set
private var dataFile: File? = null
fun length(): Long {
- if (data != null)
- return data!!.size.toLong()
if (dataFile != null)
return dataFile!!.length()
return 0
@@ -47,28 +48,12 @@ class ProtectedBinary : Parcelable {
constructor() {
this.isCompressed = null
this.isProtected = false
- this.data = null
- this.dataFile = null
- }
-
- constructor(protectedBinary: ProtectedBinary) {
- this.isCompressed = protectedBinary.isCompressed
- this.isProtected = protectedBinary.isProtected
- this.data = protectedBinary.data
- this.dataFile = protectedBinary.dataFile
- }
-
- constructor(data: ByteArray?, enableProtection: Boolean = false, compressed: Boolean? = null) {
- this.isCompressed = compressed
- this.isProtected = enableProtection
- this.data = data
this.dataFile = null
}
constructor(dataFile: File, enableProtection: Boolean = false, compressed: Boolean? = null) {
this.isCompressed = compressed
this.isProtected = enableProtection
- this.data = null
this.dataFile = dataFile
}
@@ -76,24 +61,74 @@ class ProtectedBinary : Parcelable {
val compressedByte = parcel.readByte().toInt()
isCompressed = if (compressedByte == 2) null else compressedByte != 0
isProtected = parcel.readByte().toInt() != 0
- data = ByteArray(parcel.readInt())
- parcel.readByteArray(data)
dataFile = File(parcel.readString())
}
@Throws(IOException::class)
fun getInputDataStream(): InputStream {
return when {
- data != null -> ByteArrayInputStream(data)
dataFile != null -> FileInputStream(dataFile!!)
- else -> throw IOException("Unable to get binary data")
+ // TODO
+ // else -> throw IOException("Unable to get binary data")
+ else -> ByteArrayInputStream(ByteArray(0))
}
}
+ @Throws(IOException::class)
+ fun compress() {
+ if (dataFile != null) {
+ // To compress, create a new binary with file
+ if (isCompressed != true) {
+ val fileBinaryCompress = File(dataFile!!.parent, dataFile!!.name + "_temp")
+ val outputStream = GZIPOutputStream(FileOutputStream(fileBinaryCompress))
+ readFromStream(getInputDataStream(), BUFFER_SIZE_BYTES,
+ object : ReadBytes {
+ override fun read(buffer: ByteArray) {
+ outputStream.write(buffer)
+ }
+ })
+ outputStream.close()
+
+ // Remove unGzip file
+ if (dataFile!!.delete()) {
+ if (fileBinaryCompress.renameTo(dataFile)) {
+ // Harmonize with database compression
+ isCompressed = true
+ }
+ }
+ }
+ }
+ }
+
+ @Throws(IOException::class)
+ fun decompress() {
+ if (dataFile != null) {
+ if (isCompressed != false) {
+ val fileBinaryDecompress = File(dataFile!!.parent, dataFile!!.name + "_temp")
+ val outputStream = FileOutputStream(fileBinaryDecompress)
+ readFromStream(GZIPInputStream(getInputDataStream()), BUFFER_SIZE_BYTES,
+ object : ReadBytes {
+ override fun read(buffer: ByteArray) {
+ outputStream.write(buffer)
+ }
+ })
+ outputStream.close()
+
+ // Remove gzip file
+ if (dataFile!!.delete()) {
+ if (fileBinaryDecompress.renameTo(dataFile)) {
+ // Harmonize with database compression
+ isCompressed = false
+ }
+ }
+ }
+ }
+ }
+
+ @Throws(IOException::class)
fun clear() {
- data = null
if (dataFile != null && !dataFile!!.delete())
- Log.e(TAG, "Unable to delete temp file " + dataFile!!.absolutePath)
+ throw IOException("Unable to delete temp file " + dataFile!!.absolutePath)
}
override fun equals(other: Any?): Boolean {
@@ -101,16 +136,11 @@ class ProtectedBinary : Parcelable {
return true
if (other == null || javaClass != other.javaClass)
return false
- if (other !is ProtectedBinary)
+ if (other !is BinaryAttachment)
return false
var sameData = false
- if (data != null && Arrays.equals(data, other.data))
- sameData = true
- else if (dataFile != null && dataFile == other.dataFile)
- sameData = true
- else if (data == null && other.data == null
- && dataFile == null && other.dataFile == null)
+ if (dataFile != null && dataFile == other.dataFile)
sameData = true
return isCompressed == other.isCompressed
@@ -124,7 +154,6 @@ class ProtectedBinary : Parcelable {
result = 31 * result + if (isCompressed == null) 2 else if (isCompressed!!) 1 else 0
result = 31 * result + if (isProtected) 1 else 0
result = 31 * result + dataFile!!.hashCode()
- result = 31 * result + Arrays.hashCode(data)
return result
}
@@ -135,22 +164,20 @@ class ProtectedBinary : Parcelable {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeByte((if (isCompressed == null) 2 else if (isCompressed!!) 1 else 0).toByte())
dest.writeByte((if (isProtected) 1 else 0).toByte())
- dest.writeInt(data?.size ?: 0)
- dest.writeByteArray(data)
dest.writeString(dataFile?.absolutePath)
}
companion object {
- private val TAG = ProtectedBinary::class.java.name
+ private val TAG = BinaryAttachment::class.java.name
@JvmField
- val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
- override fun createFromParcel(parcel: Parcel): ProtectedBinary {
- return ProtectedBinary(parcel)
+ val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): BinaryAttachment {
+ return BinaryAttachment(parcel)
}
- override fun newArray(size: Int): Array {
+ override fun newArray(size: Int): Array {
return arrayOfNulls(size)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt
index 25a406e0c..a85026438 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt
@@ -26,8 +26,7 @@ import com.kunzisoft.keepass.crypto.StreamCipherFactory
import com.kunzisoft.keepass.crypto.engine.CipherEngine
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BASE_64_FLAG
-import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BUFFER_SIZE_BYTES
-import com.kunzisoft.keepass.database.element.security.ProtectedBinary
+import com.kunzisoft.keepass.database.element.security.BinaryAttachment
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.exception.*
import com.kunzisoft.keepass.database.file.KDBX4DateUtil
@@ -44,6 +43,7 @@ import java.nio.charset.Charset
import java.text.ParseException
import java.util.*
import java.util.zip.GZIPInputStream
+import java.util.zip.GZIPOutputStream
import javax.crypto.Cipher
import kotlin.math.min
@@ -56,7 +56,7 @@ class ImporterV4(private val streamDir: File,
private var hashOfHeader: ByteArray? = null
private val unusedCacheFileName: String
- get() = mDatabase.binPool.findUnusedKey().toString()
+ get() = mDatabase.binaryPool.findUnusedKey().toString()
private var readNextNode = true
private val ctxGroups = Stack()
@@ -65,7 +65,7 @@ class ImporterV4(private val streamDir: File,
private var ctxStringName: String? = null
private var ctxStringValue: ProtectedString? = null
private var ctxBinaryName: String? = null
- private var ctxBinaryValue: ProtectedBinary? = null
+ private var ctxBinaryValue: BinaryAttachment? = null
private var ctxATName: String? = null
private var ctxATSeq: String? = null
private var entryInHistory = false
@@ -242,8 +242,8 @@ class ImporterV4(private val streamDir: File,
}
})
}
- val protectedBinary = ProtectedBinary(file, protectedFlag)
- mDatabase.binPool.add(protectedBinary)
+ val protectedBinary = BinaryAttachment(file, protectedFlag)
+ mDatabase.binaryPool.add(protectedBinary)
}
else -> {
return false
@@ -433,7 +433,7 @@ class ImporterV4(private val streamDir: File,
if (key != null) {
val pbData = readBinary(xpp)
val id = Integer.parseInt(key)
- mDatabase.binPool.put(id, pbData!!)
+ mDatabase.binaryPool.put(id, pbData!!)
} else {
readUnknown(xpp)
}
@@ -938,7 +938,7 @@ class ImporterV4(private val streamDir: File,
}
@Throws(XmlPullParserException::class, IOException::class)
- private fun readBinary(xpp: XmlPullParser): ProtectedBinary? {
+ private fun readBinary(xpp: XmlPullParser): BinaryAttachment? {
// Reference Id to a binary already present in binary pool
val ref = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrRef)
@@ -946,7 +946,7 @@ class ImporterV4(private val streamDir: File,
xpp.next() // Consume end tag
val id = Integer.parseInt(ref)
- return mDatabase.binPool[id]
+ return mDatabase.binaryPool[id]
}
// New binary to retrieve
@@ -968,18 +968,20 @@ class ImporterV4(private val streamDir: File,
val base64 = readString(xpp)
if (base64.isEmpty())
- return ProtectedBinary()
+ return BinaryAttachment()
val data = Base64.decode(base64, BASE_64_FLAG)
- return if (data.size <= BUFFER_SIZE_BYTES) {
- // Small data, don't need a file
- ProtectedBinary(data, protected, compressed)
- } else {
- val file = File(streamDir, unusedCacheFileName)
- FileOutputStream(file).use { outputStream ->
+ val file = File(streamDir, unusedCacheFileName)
+ return FileOutputStream(file).use { outputStream ->
+ // Force compression in this specific case
+ if (mDatabase.compressionAlgorithm == PwCompressionAlgorithm.GZip
+ && compressed == false) {
+ GZIPOutputStream(outputStream).write(data)
+ BinaryAttachment(file, protected, true)
+ } else {
outputStream.write(data)
+ BinaryAttachment(file, protected)
}
- ProtectedBinary(file, protected, compressed)
}
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt
index 9705a2736..4c3d59e85 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt
@@ -48,7 +48,7 @@ class PwDbInnerHeaderOutputV4(private val database: PwDatabaseV4,
dataOutputStream.writeInt(streamKeySize)
dataOutputStream.write(header.innerRandomStreamKey)
- database.binPool.doForEachBinary { _, protectedBinary ->
+ database.binaryPool.doForEachBinary { _, protectedBinary ->
var flag = PwDbHeaderV4.KdbxBinaryFlags.None
if (protectedBinary.isProtected) {
flag = flag or PwDbHeaderV4.KdbxBinaryFlags.Protected
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt
index f039c2a45..c452eaba6 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt
@@ -31,7 +31,7 @@ import com.kunzisoft.keepass.database.NodeHandler
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BASE_64_FLAG
import com.kunzisoft.keepass.database.element.PwDatabaseV4.Companion.BUFFER_SIZE_BYTES
-import com.kunzisoft.keepass.database.element.security.ProtectedBinary
+import com.kunzisoft.keepass.database.element.security.BinaryAttachment
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.database.exception.UnknownKDF
@@ -46,6 +46,7 @@ import java.io.*
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.util.*
+import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
@@ -217,9 +218,10 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4,
writeUuid(PwDatabaseV4XML.ElemLastSelectedGroup, mDatabaseV4.lastSelectedGroupUUID)
writeUuid(PwDatabaseV4XML.ElemLastTopVisibleGroup, mDatabaseV4.lastTopVisibleGroupUUID)
- if (header!!.version < PwDbHeaderV4.FILE_VERSION_32_4) {
- writeBinariesKDBX31()
- }
+ // Seem to work properly if always in meta
+ // if (header!!.version < PwDbHeaderV4.FILE_VERSION_32_4)
+ writeMetaBinaries()
+
writeCustomData(mDatabaseV4.customData)
xml.endTag(null, PwDatabaseV4XML.ElemMeta)
@@ -405,14 +407,15 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4,
writeObject(name, String(Base64.encode(data, BASE_64_FLAG)))
}
- // Only for KDBX3.1-
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
- private fun writeBinariesKDBX31() {
+ private fun writeMetaBinaries() {
xml.startTag(null, PwDatabaseV4XML.ElemBinaries)
- mDatabaseV4.binPool.doForEachBinary { key, binary ->
+ mDatabaseV4.binaryPool.doForEachBinary { key, binary ->
xml.startTag(null, PwDatabaseV4XML.ElemBinary)
xml.attribute(null, PwDatabaseV4XML.AttrId, key.toString())
+
+ // Force binary compression from database (compression was harmonized during import)
xml.attribute(null, PwDatabaseV4XML.AttrCompressed,
if (mDatabaseV4.compressionAlgorithm === PwCompressionAlgorithm.GZip) {
PwDatabaseV4XML.ValTrue
@@ -420,23 +423,17 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4,
PwDatabaseV4XML.ValFalse
}
)
- /*
- // TODO compression needed here ?
- // Binary need to be compressed before storage
- val byteArrayOutputStream = ByteArrayOutputStream()
- val gzipOutputStream = GZIPOutputStream(byteArrayOutputStream)
- IOUtils.copy(binary.getInputDataStream(), gzipOutputStream)
- gzipOutputStream.close()
- readFromStream(ByteArrayInputStream(byteArrayOutputStream.toByteArray()), BUFFER_SIZE_BYTES,
- object : ReadBytes {
- override fun read(buffer: ByteArray) {
- xml.text(String(Base64.encode(buffer, BASE_64_FLAG)))
- }
- }
- )
- }*/
- readFromStream(binary.getInputDataStream(), BUFFER_SIZE_BYTES,
+ // Force decompression in this specific case
+ val binaryInputStream = if (mDatabaseV4.compressionAlgorithm == PwCompressionAlgorithm.None
+ && binary.isCompressed == true) {
+ GZIPInputStream(binary.getInputDataStream())
+ } else {
+ binary.getInputDataStream()
+ }
+
+ // Write the XML
+ readFromStream(binaryInputStream, BUFFER_SIZE_BYTES,
object : ReadBytes {
override fun read(buffer: ByteArray) {
val charArray = String(Base64.encode(buffer, BASE_64_FLAG)).toCharArray()
@@ -541,7 +538,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4,
}
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
- private fun writeEntryBinaries(binaries: Map) {
+ private fun writeEntryBinaries(binaries: Map) {
for ((key, binary) in binaries) {
xml.startTag(null, PwDatabaseV4XML.ElemBinary)
xml.startTag(null, PwDatabaseV4XML.ElemKey)
@@ -549,7 +546,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4,
xml.endTag(null, PwDatabaseV4XML.ElemKey)
xml.startTag(null, PwDatabaseV4XML.ElemValue)
- val ref = mDatabaseV4.binPool.findKey(binary)
+ val ref = mDatabaseV4.binaryPool.findKey(binary)
if (ref != null) {
xml.attribute(null, PwDatabaseV4XML.AttrRef, ref.toString())
} else {
diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt
index 56fedf46b..0edb42bde 100644
--- a/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt
@@ -8,10 +8,7 @@ import android.os.Bundle
import android.os.IBinder
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
-import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable
-import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
-import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
-import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
+import com.kunzisoft.keepass.database.action.*
import com.kunzisoft.keepass.database.action.node.*
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.settings.PreferencesUtil
@@ -107,11 +104,11 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
ACTION_DATABASE_COPY_NODES_TASK -> buildDatabaseCopyNodesActionTask(intent)
ACTION_DATABASE_MOVE_NODES_TASK -> buildDatabaseMoveNodesActionTask(intent)
ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent)
+ ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> buildDatabaseUpdateCompressionActionTask(intent)
ACTION_DATABASE_UPDATE_NAME_TASK,
ACTION_DATABASE_UPDATE_DESCRIPTION_TASK,
ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK,
ACTION_DATABASE_UPDATE_COLOR_TASK,
- ACTION_DATABASE_UPDATE_COMPRESSION_TASK,
ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK,
ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK,
ACTION_DATABASE_UPDATE_ENCRYPTION_TASK,
@@ -409,6 +406,25 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
}
}
+ private fun buildDatabaseUpdateCompressionActionTask(intent: Intent): ActionRunnable? {
+ return if (intent.hasExtra(OLD_ELEMENT_KEY)
+ && intent.hasExtra(NEW_ELEMENT_KEY)
+ && intent.hasExtra(SAVE_DATABASE_KEY)) {
+ return UpdateCompressionBinariesDatabaseRunnable(this,
+ Database.getInstance(),
+ intent.getParcelableExtra(OLD_ELEMENT_KEY),
+ intent.getParcelableExtra(NEW_ELEMENT_KEY),
+ intent.getBooleanExtra(SAVE_DATABASE_KEY, false)
+ ).apply {
+ mAfterSaveDatabase = { result ->
+ result.data = intent.extras
+ }
+ }
+ } else {
+ null
+ }
+ }
+
private fun buildDatabaseUpdateElementActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(SAVE_DATABASE_KEY)) {
return SaveDatabaseRunnable(this,
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt
index c37c5cdc1..ada214fc3 100644
--- a/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt
@@ -96,3 +96,9 @@ object ParcelableUtil {
return map
}
}
+
+inline fun > Parcel.readEnum() =
+ readString()?.let { enumValueOf(it) }
+
+inline fun > Parcel.writeEnum(value: T?) =
+ writeString(value?.name)