Change save binaries compression for database 3.1 & 4

This commit is contained in:
J-Jamet
2020-08-27 13:49:48 +02:00
parent 2191a4a848
commit dd8d114711
6 changed files with 46 additions and 57 deletions

View File

@@ -72,6 +72,14 @@ class BinaryAttachment : Parcelable {
} }
} }
@Throws(IOException::class)
fun getUnGzipInputDataStream(): InputStream {
return if (isCompressed)
GZIPInputStream(getInputDataStream())
else
getInputDataStream()
}
@Throws(IOException::class) @Throws(IOException::class)
fun getOutputDataStream(): OutputStream { fun getOutputDataStream(): OutputStream {
return when { return when {
@@ -110,7 +118,7 @@ class BinaryAttachment : Parcelable {
if (isCompressed) { if (isCompressed) {
val fileBinaryDecompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp") val fileBinaryDecompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp")
FileOutputStream(fileBinaryDecompress).use { outputStream -> FileOutputStream(fileBinaryDecompress).use { outputStream ->
GZIPInputStream(getInputDataStream()).use { inputStream -> getUnGzipInputDataStream().use { inputStream ->
inputStream.readBytes(bufferSize) { buffer -> inputStream.readBytes(bufferSize) { buffer ->
outputStream.write(buffer) outputStream.write(buffer)
} }

View File

@@ -57,7 +57,7 @@ class BinaryPool {
binaryAttachment.clear() binaryAttachment.clear()
} }
fun findUnusedKey(): Int { private fun findUnusedKey(): Int {
var unusedKey = 0 var unusedKey = 0
while (pool[unusedKey] != null) while (pool[unusedKey] != null)
unusedKey++ unusedKey++

View File

@@ -233,8 +233,10 @@ class DatabaseInputKDBX(cacheDirectory: File,
var data = ByteArray(0) var data = ByteArray(0)
if (size > 0) { if (size > 0) {
if (fieldId != DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary) if (fieldId != DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary) {
// TODO OOM here
data = dataInputStream.readBytes(size) data = dataInputStream.readBytes(size)
}
} }
var result = true var result = true
@@ -249,12 +251,11 @@ class DatabaseInputKDBX(cacheDirectory: File,
header.innerRandomStreamKey = data header.innerRandomStreamKey = data
} }
DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary -> { DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary -> {
val byteLength = size - 1
// Read in a file // Read in a file
val protectedFlag = dataInputStream.readBytes(1)[0].toInt() != 0 val protectedFlag = dataInputStream.readBytes(1)[0].toInt() != 0
// Unknown compression at this level val byteLength = size - 1
val compression = mDatabase.compressionAlgorithm == CompressionAlgorithm.GZip // No compression at this level
val protectedBinary = mDatabase.buildNewBinary(cacheDirectory, protectedFlag, compression) val protectedBinary = mDatabase.buildNewBinary(cacheDirectory, protectedFlag, false)
protectedBinary.getOutputDataStream().use { outputStream -> protectedBinary.getOutputDataStream().use { outputStream ->
dataInputStream.readBytes(byteLength, DatabaseKDBX.BUFFER_SIZE_BYTES) { buffer -> dataInputStream.readBytes(byteLength, DatabaseKDBX.BUFFER_SIZE_BYTES) { buffer ->
outputStream.write(buffer) outputStream.write(buffer)
@@ -940,27 +941,28 @@ class DatabaseInputKDBX(cacheDirectory: File,
// Reference Id to a binary already present in binary pool // Reference Id to a binary already present in binary pool
val ref = xpp.getAttributeValue(null, DatabaseKDBXXML.AttrRef) val ref = xpp.getAttributeValue(null, DatabaseKDBXXML.AttrRef)
if (ref != null) { // New id to a binary
xpp.next() // Consume end tag
val id = Integer.parseInt(ref)
return mDatabase.binaryPool[id]
}
val key = xpp.getAttributeValue(null, DatabaseKDBXXML.AttrId) val key = xpp.getAttributeValue(null, DatabaseKDBXXML.AttrId)
return if (key != null) {
createBinary(key.toIntOrNull(), xpp)
}
// New binary to retrieve return when {
else { ref != null -> {
createBinary(null, xpp) xpp.next() // Consume end tag
val id = Integer.parseInt(ref)
mDatabase.binaryPool[id]
}
key != null -> {
createBinary(key.toIntOrNull(), xpp)
}
else -> {
// New binary to retrieve
createBinary(null, xpp)
}
} }
} }
@Throws(IOException::class, XmlPullParserException::class) @Throws(IOException::class, XmlPullParserException::class)
private fun createBinary(binaryId: Int?, xpp: XmlPullParser): BinaryAttachment? { private fun createBinary(binaryId: Int?, xpp: XmlPullParser): BinaryAttachment? {
var compressed: Boolean = mDatabase.compressionAlgorithm == CompressionAlgorithm.GZip var compressed = false
var protected = false var protected = false
if (xpp.attributeCount > 0) { if (xpp.attributeCount > 0) {
@@ -980,10 +982,10 @@ class DatabaseInputKDBX(cacheDirectory: File,
return null return null
val data = Base64.decode(base64, BASE_64_FLAG) val data = Base64.decode(base64, BASE_64_FLAG)
// Force compression in this specific case // Build the new binary and compress
val binaryAttachment = mDatabase.buildNewBinary(cacheDirectory, protected, compressed, binaryId) val binaryAttachment = mDatabase.buildNewBinary(cacheDirectory, protected, compressed, binaryId)
binaryAttachment.getOutputDataStream().use { outputStream -> binaryAttachment.getOutputDataStream().use { outputStream ->
outputStream.write(data) outputStream.write(data)
} }
return binaryAttachment return binaryAttachment
} }

View File

@@ -54,11 +54,14 @@ class DatabaseInnerHeaderOutputKDBX(private val database: DatabaseKDBX,
} }
dataOutputStream.write(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary.toInt()) dataOutputStream.write(DatabaseHeaderKDBX.PwDbInnerHeaderV4Fields.Binary.toInt())
dataOutputStream.writeInt(protectedBinary.length().toInt() + 1) // TODO verify dataOutputStream.writeInt(protectedBinary.length().toInt() + 1)
dataOutputStream.write(flag.toInt()) dataOutputStream.write(flag.toInt())
protectedBinary.getInputDataStream().readBytes(BUFFER_SIZE_BYTES) { buffer -> // if was compressed in cache, uncompress it
dataOutputStream.write(buffer) protectedBinary.getUnGzipInputDataStream().use { inputStream ->
inputStream.readBytes(BUFFER_SIZE_BYTES) { buffer ->
dataOutputStream.write(buffer)
}
} }
} }

View File

@@ -55,7 +55,6 @@ import java.io.OutputStream
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.CipherOutputStream import javax.crypto.CipherOutputStream
@@ -422,7 +421,6 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
private fun writeBinary(binary : BinaryAttachment) { private fun writeBinary(binary : BinaryAttachment) {
val binaryLength = binary.length() val binaryLength = binary.length()
if (binaryLength > 0) { if (binaryLength > 0) {
if (binary.isProtected) { if (binary.isProtected) {
xml.attribute(null, DatabaseKDBXXML.AttrProtected, DatabaseKDBXXML.ValTrue) xml.attribute(null, DatabaseKDBXXML.AttrProtected, DatabaseKDBXXML.ValTrue)
@@ -433,21 +431,11 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
xml.text(charArray, 0, charArray.size) xml.text(charArray, 0, charArray.size)
} }
} else { } else {
// Force binary compression from database (compression was harmonized during import) if (binary.isCompressed) {
if (mDatabaseKDBX.compressionAlgorithm === CompressionAlgorithm.GZip) {
xml.attribute(null, DatabaseKDBXXML.AttrCompressed, DatabaseKDBXXML.ValTrue) xml.attribute(null, DatabaseKDBXXML.AttrCompressed, DatabaseKDBXXML.ValTrue)
} }
// Force decompression in this specific case
val binaryInputStream = if (mDatabaseKDBX.compressionAlgorithm == CompressionAlgorithm.None
&& binary.isCompressed) {
GZIPInputStream(binary.getInputDataStream())
} else {
binary.getInputDataStream()
}
// Write the XML // Write the XML
binaryInputStream.readBytes(BUFFER_SIZE_BYTES) { buffer -> binary.getInputDataStream().readBytes(BUFFER_SIZE_BYTES) { buffer ->
val charArray = String(Base64.encode(buffer, BASE_64_FLAG)).toCharArray() val charArray = String(Base64.encode(buffer, BASE_64_FLAG)).toCharArray()
xml.text(charArray, 0, charArray.size) xml.text(charArray, 0, charArray.size)
} }
@@ -560,23 +548,15 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun writeEntryBinaries(binaries: LinkedHashMap<String, Int>) { private fun writeEntryBinaries(binaries: LinkedHashMap<String, Int>) {
binaries.forEach { for ((label, poolId) in binaries) {
xml.startTag(null, DatabaseKDBXXML.ElemBinary) xml.startTag(null, DatabaseKDBXXML.ElemBinary)
xml.startTag(null, DatabaseKDBXXML.ElemKey) xml.startTag(null, DatabaseKDBXXML.ElemKey)
xml.text(safeXmlString(it.key)) xml.text(safeXmlString(label))
xml.endTag(null, DatabaseKDBXXML.ElemKey) xml.endTag(null, DatabaseKDBXXML.ElemKey)
xml.startTag(null, DatabaseKDBXXML.ElemValue) xml.startTag(null, DatabaseKDBXXML.ElemValue)
xml.attribute(null, DatabaseKDBXXML.AttrRef, it.value.toString()) // Use only pool data in Meta to save binaries
/* xml.attribute(null, DatabaseKDBXXML.AttrRef, poolId.toString())
// By default use only pool data in head to save binaries
val ref = mDatabaseKDBX.binaryPool.findKey(it.binaryAttachment)
if (ref != null) {
xml.attribute(null, DatabaseKDBXXML.AttrRef, ref.toString())
} else {
writeBinary(it.binaryAttachment)
}
*/
xml.endTag(null, DatabaseKDBXXML.ElemValue) xml.endTag(null, DatabaseKDBXXML.ElemValue)
xml.endTag(null, DatabaseKDBXXML.ElemBinary) xml.endTag(null, DatabaseKDBXXML.ElemBinary)

View File

@@ -362,11 +362,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
var dataDownloaded = 0L var dataDownloaded = 0L
val fileSize = binaryAttachment.length() val fileSize = binaryAttachment.length()
UriUtil.getUriOutputStream(contentResolver, attachmentToUploadUri)?.use { outputStream -> UriUtil.getUriOutputStream(contentResolver, attachmentToUploadUri)?.use { outputStream ->
if (binaryAttachment.isCompressed) { binaryAttachment.getUnGzipInputDataStream().use { inputStream ->
GZIPInputStream(binaryAttachment.getInputDataStream())
} else {
binaryAttachment.getInputDataStream()
}.use { inputStream ->
inputStream.readBytes(bufferSize) { buffer -> inputStream.readBytes(bufferSize) { buffer ->
outputStream.write(buffer) outputStream.write(buffer)
dataDownloaded += buffer.size dataDownloaded += buffer.size