Write only attachments in header

Remove unlinked attachments
Simpler compression
This commit is contained in:
J-Jamet
2020-08-26 18:51:25 +02:00
parent 42515bfb2d
commit efe30b598b
18 changed files with 186 additions and 135 deletions

View File

@@ -333,9 +333,11 @@ class EntryActivity : LockingActivity() {
entryContentsView?.setHiddenProtectedValue(!mShowPassword)
// Manage attachments
entryContentsView?.assignAttachments(entry.getAttachments(), StreamDirection.DOWNLOAD) { attachmentItem ->
createDocument(this, attachmentItem.name)?.let { requestCode ->
mAttachmentsToDownload[requestCode] = attachmentItem
mDatabase?.binaryPool?.let { binaryPool ->
entryContentsView?.assignAttachments(entry.getAttachments(binaryPool), StreamDirection.DOWNLOAD) { attachmentItem ->
createDocument(this, attachmentItem.name)?.let { requestCode ->
mAttachmentsToDownload[requestCode] = attachmentItem
}
}
}

View File

@@ -358,8 +358,11 @@ class EntryEditActivity : LockingActivity(),
assignExtraFields(newEntry.customFields.mapTo(ArrayList()) {
Field(it.key, it.value)
}, mFocusedEditExtraField)
assignAttachments(newEntry.getAttachments(), StreamDirection.UPLOAD) { attachment ->
newEntry.removeAttachment(attachment)
mDatabase?.binaryPool?.let { binaryPool ->
assignAttachments(newEntry.getAttachments(binaryPool), StreamDirection.UPLOAD) { attachment ->
newEntry.removeAttachment(attachment, binaryPool)
}
}
}
}
@@ -384,8 +387,10 @@ class EntryEditActivity : LockingActivity(),
entryView.getExtraFields().forEach { customField ->
putExtraField(customField.name, customField.protectedValue)
}
entryView.getAttachments().forEach {
putAttachment(it)
mDatabase?.binaryPool?.let { binaryPool ->
entryView.getAttachments().forEach {
putAttachment(it, binaryPool)
}
}
mFocusedEditExtraField = entryView.getExtraFieldFocused()
}

View File

@@ -337,8 +337,10 @@ class NodeAdapter (private val context: Context)
setTextSize(textSizeUnit, subtextDefaultDimension, prefSizeMultiplier)
}
}
holder.attachmentIcon?.visibility =
if (entry.getAttachments().isNotEmpty()) View.VISIBLE else View.GONE
if (entry.getAttachments(mDatabase.binaryPool).isNotEmpty())
View.VISIBLE else View.GONE
mDatabase.stopManageEntry(entry)
}

View File

@@ -34,6 +34,7 @@ open class SaveDatabaseRunnable(protected var context: Context,
override fun onStartRun() {}
override fun onActionRun() {
database.removeUnlinkedAttachment()
if (saveDatabase && result.isSuccess) {
try {
database.saveData(context.contentResolver)

View File

@@ -46,7 +46,7 @@ class UpdateEntryRunnable constructor(
// Create an entry history (an entry history don't have history)
mOldEntry.addEntryToHistory(Entry(mBackupEntryHistory, copyHistory = false))
database.removeOldestEntryHistory(mOldEntry)
database.removeOldestEntryHistory(mOldEntry, database.binaryPool)
// Only change data in index
database.updateEntry(mOldEntry)

View File

@@ -25,9 +25,7 @@ import android.net.Uri
import android.util.Log
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.database.action.node.NodeHandler
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
import com.kunzisoft.keepass.database.element.database.*
import com.kunzisoft.keepass.database.element.icon.IconImageFactory
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.NodeIdInt
@@ -430,6 +428,11 @@ class Database {
}, omitBackup, max)
}
val binaryPool: BinaryPool
get() {
return mDatabaseKDBX?.binaryPool ?: BinaryPool()
}
val allowMultipleAttachments: Boolean
get() {
if (mDatabaseKDB != null)
@@ -441,7 +444,7 @@ class Database {
fun buildNewBinary(cacheDirectory: File,
enableProtection: Boolean = false,
compressed: Boolean? = null): BinaryAttachment? {
compressed: Boolean = false): BinaryAttachment? {
return mDatabaseKDB?.buildNewBinary(cacheDirectory)
?: mDatabaseKDBX?.buildNewBinary(cacheDirectory, enableProtection, compressed)
}
@@ -828,7 +831,7 @@ class Database {
rootGroup?.doForEachChildAndForIt(
object : NodeHandler<Entry>() {
override fun operate(node: Entry): Boolean {
removeOldestEntryHistory(node)
removeOldestEntryHistory(node, binaryPool)
return true
}
},
@@ -836,7 +839,8 @@ class Database {
override fun operate(node: Group): Boolean {
return true
}
})
}
)
}
fun removeEachEntryHistory() {
@@ -857,9 +861,8 @@ class Database {
/**
* Remove oldest history if more than max items or max memory
*/
fun removeOldestEntryHistory(entry: Entry) {
fun removeOldestEntryHistory(entry: Entry, binaryPool: BinaryPool) {
mDatabaseKDBX?.let {
val maxItems = historyMaxItems
if (maxItems >= 0) {
while (entry.getHistory().size > maxItems) {
@@ -872,7 +875,7 @@ class Database {
while (true) {
var historySize: Long = 0
for (entryHistory in entry.getHistory()) {
historySize += entryHistory.getSize()
historySize += entryHistory.getSize(binaryPool)
}
if (historySize > maxSize) {

View File

@@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.element
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.database.element.database.BinaryPool
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
import com.kunzisoft.keepass.database.element.entry.EntryKDB
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
@@ -316,38 +317,38 @@ class Entry : Node, EntryVersionedInterface<Group> {
}
}
fun startToManageFieldReferences(db: DatabaseKDBX) {
entryKDBX?.startToManageFieldReferences(db)
fun startToManageFieldReferences(database: DatabaseKDBX) {
entryKDBX?.startToManageFieldReferences(database)
}
fun stopToManageFieldReferences() {
entryKDBX?.stopToManageFieldReferences()
}
override fun getAttachments(): ArrayList<Attachment> {
fun getAttachments(binaryPool: BinaryPool): ArrayList<Attachment> {
val attachments = ArrayList<Attachment>()
entryKDB?.getAttachments()?.let {
attachments.addAll(it)
}
entryKDBX?.getAttachments()?.let {
entryKDBX?.getAttachments(binaryPool)?.let {
attachments.addAll(it)
}
return attachments
}
override fun containsAttachment(attachment: Attachment): Boolean {
fun containsAttachment(attachment: Attachment, binaryPool: BinaryPool): Boolean {
return entryKDB?.containsAttachment(attachment) == true
|| entryKDBX?.containsAttachment(attachment) == true
|| entryKDBX?.containsAttachment(attachment, binaryPool) == true
}
override fun putAttachment(attachment: Attachment) {
fun putAttachment(attachment: Attachment, binaryPool: BinaryPool) {
entryKDB?.putAttachment(attachment)
entryKDBX?.putAttachment(attachment)
entryKDBX?.putAttachment(attachment, binaryPool)
}
override fun removeAttachment(attachment: Attachment) {
fun removeAttachment(attachment: Attachment, binaryPool: BinaryPool) {
entryKDB?.removeAttachment(attachment)
entryKDBX?.removeAttachment(attachment)
entryKDBX?.removeAttachment(attachment, binaryPool)
}
fun getHistory(): ArrayList<Entry> {
@@ -377,8 +378,8 @@ class Entry : Node, EntryVersionedInterface<Group> {
entryKDBX?.removeOldestEntryFromHistory()
}
fun getSize(): Long {
return entryKDBX?.size ?: 0L
fun getSize(binaryPool: BinaryPool): Long {
return entryKDBX?.getSize(binaryPool) ?: 0L
}
fun containsCustomData(): Boolean {

View File

@@ -19,26 +19,32 @@
*/
package com.kunzisoft.keepass.database.element.database
import android.util.SparseArray
import androidx.core.util.forEach
import com.kunzisoft.keepass.database.element.security.BinaryAttachment
import java.io.IOException
class BinaryPool {
private val pool = SparseArray<BinaryAttachment>()
private val pool = LinkedHashMap<Int, BinaryAttachment>()
operator fun get(key: Int): BinaryAttachment? {
return pool[key]
}
fun put(key: Int, value: BinaryAttachment) {
pool.put(key, value)
pool[key] = value
}
fun add(binaryAttachment: BinaryAttachment) {
if (findKey(binaryAttachment) == null) {
pool.put(findUnusedKey(), binaryAttachment)
/**
* To put a [binaryAttachment] in the pool,
* if already exists, replace the current one,
* else add it with a new key
*/
fun put(binaryAttachment: BinaryAttachment): Int {
var key = findKey(binaryAttachment)
if (key == null) {
key = findUnusedKey()
}
pool[key] = binaryAttachment
return key
}
@Throws(IOException::class)
@@ -57,26 +63,39 @@ class BinaryPool {
}
/**
* Return position of [binaryAttachmentToRetrieve] or null if not found
* Return key of [binaryAttachmentToRetrieve] or null if not found
*/
fun findKey(binaryAttachmentToRetrieve: BinaryAttachment): Int? {
val index = pool.indexOfValue(binaryAttachmentToRetrieve)
return if (index < 0)
private fun findKey(binaryAttachmentToRetrieve: BinaryAttachment): Int? {
val contains = pool.containsValue(binaryAttachmentToRetrieve)
return if (!contains)
null
else
pool.keyAt(index)
else {
for ((key, binary) in pool) {
if (binary == binaryAttachmentToRetrieve) {
return key
}
}
return null
}
}
fun doForEachBinary(action: (key: Int, binary: BinaryAttachment) -> Unit) {
pool.forEach { key, binaryAttachment ->
action.invoke(key, binaryAttachment)
/**
* Warning 2 keys can point the same binary
*/
fun doForEach(action: (key: Int, binary: BinaryAttachment) -> Unit) {
for ((key, binary) in pool) {
action.invoke(key, binary)
}
}
fun doForEachBinary(action: (binary: BinaryAttachment) -> Unit) {
pool.values.toSet().forEach { action.invoke(it) }
}
@Throws(IOException::class)
fun clear() {
pool.forEach { _, binary ->
binary.clear()
doForEachBinary {
it.clear()
}
pool.clear()
}

View File

@@ -177,8 +177,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
fun changeBinaryCompression(oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm) {
binaryPool.doForEachBinary { key, binary ->
binaryPool.doForEachBinary { binary ->
try {
when (oldCompression) {
CompressionAlgorithm.None -> {
@@ -203,7 +202,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
}
}
} catch (e: Exception) {
Log.e(TAG, "Unable to change compression for $key", e)
Log.e(TAG, "Unable to change compression for $binary", e)
}
}
}
@@ -542,7 +541,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
fun buildNewBinary(cacheDirectory: File,
protection: Boolean,
compression: Boolean?,
compression: Boolean,
cacheId: String? = null): BinaryAttachment {
// Unused cache key if needed
val binaryId = cacheId ?: binaryPool.findUnusedKey().toString()
@@ -558,12 +557,11 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
removeUnlinkedAttachment(attachment.binaryAttachment)
}
// TODO buf unlink right element
fun removeUnlinkedAttachment(vararg binaries: BinaryAttachment) {
// Build binaries to remove with all binaries known
val binariesToRemove = ArrayList<BinaryAttachment>()
if (binaries.isEmpty()) {
binaryPool.doForEachBinary { _, binary ->
binaryPool.doForEachBinary { binary ->
binariesToRemove.add(binary)
}
} else {
@@ -572,7 +570,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
// Remove binaries from the list
rootGroup?.doForEachChild(object : NodeHandler<EntryKDBX>() {
override fun operate(node: EntryKDBX): Boolean {
node.getAttachments().forEach {
node.getAttachments(binaryPool).forEach {
binariesToRemove.remove(it.binaryAttachment)
}
return binariesToRemove.isNotEmpty()

View File

@@ -137,7 +137,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
override val type: Type
get() = Type.ENTRY
override fun getAttachments(): ArrayList<Attachment> {
fun getAttachments(): ArrayList<Attachment> {
return ArrayList<Attachment>().apply {
val binary = binaryData
if (binary != null)
@@ -145,16 +145,16 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
}
}
override fun containsAttachment(attachment: Attachment): Boolean {
fun containsAttachment(attachment: Attachment): Boolean {
return binaryData != null
}
override fun putAttachment(attachment: Attachment) {
fun putAttachment(attachment: Attachment) {
this.binaryDescription = attachment.name
this.binaryData = attachment.binaryAttachment
}
override fun removeAttachment(attachment: Attachment) {
fun removeAttachment(attachment: Attachment) {
if (this.binaryDescription == attachment.name) {
this.binaryDescription = ""
this.binaryData = null

View File

@@ -31,9 +31,9 @@ import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.database.element.security.BinaryAttachment
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.element.Attachment
import com.kunzisoft.keepass.database.element.database.BinaryPool
import com.kunzisoft.keepass.utils.ParcelableUtil
import com.kunzisoft.keepass.utils.UnsignedLong
import java.util.*
@@ -64,7 +64,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
private var customData = LinkedHashMap<String, String>()
// TODO Private
var fields = LinkedHashMap<String, ProtectedString>()
private var binaries = LinkedHashMap<String, BinaryAttachment>()
var binaries = LinkedHashMap<String, Int>() // Map<Label, PoolId>
var foregroundColor = ""
var backgroundColor = ""
var overrideURL = ""
@@ -73,36 +73,32 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
var additional = ""
var tags = ""
val size: Long
get() {
var size = FIXED_LENGTH_SIZE
fun getSize(binaryPool: BinaryPool): Long {
var size = FIXED_LENGTH_SIZE
for (entry in fields.entries) {
size += entry.key.length.toLong()
size += entry.value.length().toLong()
}
for ((key, value) in binaries) {
size += key.length.toLong()
size += value.length()
}
size += autoType.defaultSequence.length.toLong()
for ((key, value) in autoType.entrySet()) {
size += key.length.toLong()
size += value.length.toLong()
}
for (entry in history) {
size += entry.size
}
size += overrideURL.length.toLong()
size += tags.length.toLong()
return size
for (entry in fields.entries) {
size += entry.key.length.toLong()
size += entry.value.length().toLong()
}
size += getAttachmentsSize(binaryPool)
size += autoType.defaultSequence.length.toLong()
for ((key, value) in autoType.entrySet()) {
size += key.length.toLong()
size += value.length.toLong()
}
for (entry in history) {
size += entry.getSize(binaryPool)
}
size += overrideURL.length.toLong()
size += tags.length.toLong()
return size
}
override var expires: Boolean = false
constructor() : super()
@@ -113,7 +109,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged
customData = ParcelableUtil.readStringParcelableMap(parcel)
fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
binaries = ParcelableUtil.readStringParcelableMap(parcel, BinaryAttachment::class.java)
binaries = ParcelableUtil.readStringIntMap(parcel)
foregroundColor = parcel.readString() ?: foregroundColor
backgroundColor = parcel.readString() ?: backgroundColor
overrideURL = parcel.readString() ?: overrideURL
@@ -131,7 +127,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
dest.writeParcelable(locationChanged, flags)
ParcelableUtil.writeStringParcelableMap(dest, customData)
ParcelableUtil.writeStringParcelableMap(dest, flags, fields)
ParcelableUtil.writeStringParcelableMap(dest, flags, binaries)
ParcelableUtil.writeStringIntMap(dest, binaries)
dest.writeString(foregroundColor)
dest.writeString(backgroundColor)
dest.writeString(overrideURL)
@@ -170,8 +166,8 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
tags = source.tags
}
fun startToManageFieldReferences(db: DatabaseKDBX) {
this.mDatabase = db
fun startToManageFieldReferences(database: DatabaseKDBX) {
this.mDatabase = database
this.mDecodeRef = true
}
@@ -287,28 +283,40 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
fields[label] = value
}
override fun getAttachments(): ArrayList<Attachment> {
fun getAttachments(binaryPool: BinaryPool): ArrayList<Attachment> {
val entryAttachmentList = ArrayList<Attachment>()
for ((key, value) in binaries) {
entryAttachmentList.add(Attachment(key, value))
for ((label, poolId) in binaries) {
binaryPool[poolId]?.let { binary ->
entryAttachmentList.add(Attachment(label, binary))
}
}
return entryAttachmentList
}
override fun containsAttachment(attachment: Attachment): Boolean {
for ((_, binary) in binaries) {
if (binary == attachment.binaryAttachment)
return true
fun containsAttachment(attachment: Attachment, binaryPool: BinaryPool): Boolean {
for ((_, poolId) in binaries) {
if (binaryPool[poolId] == attachment.binaryAttachment)
return true
}
return false
}
override fun putAttachment(attachment: Attachment) {
binaries[attachment.name] = attachment.binaryAttachment
fun putAttachment(attachment: Attachment, binaryPool: BinaryPool) {
binaries[attachment.name] = binaryPool.put(attachment.binaryAttachment)
}
override fun removeAttachment(attachment: Attachment) {
fun removeAttachment(attachment: Attachment, binaryPool: BinaryPool) {
binaries.remove(attachment.name)
binaryPool.remove(attachment.binaryAttachment)
}
private fun getAttachmentsSize(binaryPool: BinaryPool): Long {
var size = 0L
for ((label, poolId) in binaries) {
size += label.length.toLong()
size += binaryPool[poolId]?.length() ?: 0
}
return size
}
// TODO Remove ?

View File

@@ -20,7 +20,6 @@
package com.kunzisoft.keepass.database.element.entry
import com.kunzisoft.keepass.database.element.node.NodeVersionedInterface
import com.kunzisoft.keepass.database.element.Attachment
interface EntryVersionedInterface<ParentGroup> : NodeVersionedInterface<ParentGroup> {
@@ -31,12 +30,4 @@ interface EntryVersionedInterface<ParentGroup> : NodeVersionedInterface<ParentGr
var url: String
var notes: String
fun getAttachments(): ArrayList<Attachment>
fun containsAttachment(attachment: Attachment): Boolean
fun putAttachment(attachment: Attachment)
fun removeAttachment(attachment: Attachment)
}

View File

@@ -28,7 +28,7 @@ import java.util.zip.GZIPOutputStream
class BinaryAttachment : Parcelable {
var isCompressed: Boolean? = null
var isCompressed: Boolean = false
private set
var isProtected: Boolean = false
private set
@@ -44,20 +44,19 @@ class BinaryAttachment : Parcelable {
* Empty protected binary
*/
constructor() {
this.isCompressed = null
this.isCompressed = false
this.isProtected = false
this.dataFile = null
}
constructor(dataFile: File, enableProtection: Boolean = false, compressed: Boolean? = null) {
constructor(dataFile: File, enableProtection: Boolean = false, compressed: Boolean = false) {
this.isCompressed = compressed
this.isProtected = enableProtection
this.dataFile = dataFile
}
private constructor(parcel: Parcel) {
val compressedByte = parcel.readByte().toInt()
isCompressed = if (compressedByte == 2) null else compressedByte != 0
isCompressed = parcel.readByte().toInt() != 0
isProtected = parcel.readByte().toInt() != 0
parcel.readString()?.let {
dataFile = File(it)
@@ -84,7 +83,7 @@ class BinaryAttachment : Parcelable {
fun compress(bufferSize: Int = DEFAULT_BUFFER_SIZE) {
dataFile?.let { concreteDataFile ->
// To compress, create a new binary with file
if (isCompressed != true) {
if (!isCompressed) {
val fileBinaryCompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp")
GZIPOutputStream(FileOutputStream(fileBinaryCompress)).use { outputStream ->
getInputDataStream().use { inputStream ->
@@ -100,8 +99,6 @@ class BinaryAttachment : Parcelable {
isCompressed = true
}
}
} else {
isCompressed = true
}
}
}
@@ -109,7 +106,7 @@ class BinaryAttachment : Parcelable {
@Throws(IOException::class)
fun decompress(bufferSize: Int = DEFAULT_BUFFER_SIZE) {
dataFile?.let { concreteDataFile ->
if (isCompressed != false) {
if (isCompressed) {
val fileBinaryDecompress = File(concreteDataFile.parent, concreteDataFile.name + "_temp")
FileOutputStream(fileBinaryDecompress).use { outputStream ->
GZIPInputStream(getInputDataStream()).use { inputStream ->
@@ -125,8 +122,6 @@ class BinaryAttachment : Parcelable {
isCompressed = false
}
}
} else {
isCompressed = false
}
}
}
@@ -157,7 +152,7 @@ class BinaryAttachment : Parcelable {
override fun hashCode(): Int {
var result = 0
result = 31 * result + if (isCompressed == null) 2 else if (isCompressed!!) 1 else 0
result = 31 * result + if (isCompressed) 1 else 0
result = 31 * result + if (isProtected) 1 else 0
result = 31 * result + dataFile!!.hashCode()
return result
@@ -172,7 +167,7 @@ class BinaryAttachment : Parcelable {
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeByte((if (isCompressed == null) 2 else if (isCompressed!!) 1 else 0).toByte())
dest.writeByte((if (isCompressed) 1 else 0).toByte())
dest.writeByte((if (isProtected) 1 else 0).toByte())
dest.writeString(dataFile?.absolutePath)
}

View File

@@ -757,8 +757,9 @@ class DatabaseInputKDBX(cacheDirectory: File,
return KdbContext.Entry
} else if (ctx == KdbContext.EntryBinary && name.equals(DatabaseKDBXXML.ElemBinary, ignoreCase = true)) {
if (ctxBinaryName != null && ctxBinaryValue != null)
ctxEntry?.putAttachment(Attachment(ctxBinaryName!!, ctxBinaryValue!!))
if (ctxBinaryName != null && ctxBinaryValue != null) {
ctxEntry?.putAttachment(Attachment(ctxBinaryName!!, ctxBinaryValue!!), mDatabase.binaryPool)
}
ctxBinaryName = null
ctxBinaryValue = null

View File

@@ -47,7 +47,7 @@ class DatabaseInnerHeaderOutputKDBX(private val database: DatabaseKDBX,
dataOutputStream.writeInt(streamKeySize)
dataOutputStream.write(header.innerRandomStreamKey)
database.binaryPool.doForEachBinary { _, protectedBinary ->
database.binaryPool.doForEachBinary { protectedBinary ->
var flag = DatabaseHeaderKDBX.KdbxBinaryFlags.None
if (protectedBinary.isProtected) {
flag = flag or DatabaseHeaderKDBX.KdbxBinaryFlags.Protected

View File

@@ -46,7 +46,6 @@ import com.kunzisoft.keepass.database.exception.UnknownKDF
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
import com.kunzisoft.keepass.database.file.DateKDBXUtil
import com.kunzisoft.keepass.database.element.Attachment
import com.kunzisoft.keepass.stream.*
import org.bouncycastle.crypto.StreamCipher
import org.joda.time.DateTime
@@ -360,7 +359,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
writeTimes(entry)
writeFields(entry.fields)
writeEntryBinaries(entry.getAttachments())
writeEntryBinaries(entry.binaries)
writeAutoType(entry.autoType)
if (!isHistory) {
@@ -441,7 +440,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
// Force decompression in this specific case
val binaryInputStream = if (mDatabaseKDBX.compressionAlgorithm == CompressionAlgorithm.None
&& binary.isCompressed == true) {
&& binary.isCompressed) {
GZIPInputStream(binary.getInputDataStream())
} else {
binary.getInputDataStream()
@@ -460,7 +459,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
private fun writeMetaBinaries() {
xml.startTag(null, DatabaseKDBXXML.ElemBinaries)
mDatabaseKDBX.binaryPool.doForEachBinary { key, binary ->
mDatabaseKDBX.binaryPool.doForEach { key, binary ->
xml.startTag(null, DatabaseKDBXXML.ElemBinary)
xml.attribute(null, DatabaseKDBXXML.AttrId, key.toString())
writeBinary(binary)
@@ -560,20 +559,24 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
}
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
private fun writeEntryBinaries(attachments: List<Attachment>) {
attachments.forEach {
private fun writeEntryBinaries(binaries: LinkedHashMap<String, Int>) {
binaries.forEach {
xml.startTag(null, DatabaseKDBXXML.ElemBinary)
xml.startTag(null, DatabaseKDBXXML.ElemKey)
xml.text(safeXmlString(it.name))
xml.text(safeXmlString(it.key))
xml.endTag(null, DatabaseKDBXXML.ElemKey)
xml.startTag(null, DatabaseKDBXXML.ElemValue)
xml.attribute(null, DatabaseKDBXXML.AttrRef, it.value.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.ElemBinary)

View File

@@ -47,7 +47,7 @@ import java.util.zip.GZIPOutputStream
class AttachmentFileNotificationService: LockNotificationService() {
override val notificationId: Int = 10000
private val attachmentNotificationList = ArrayList<AttachmentNotification>()
private val attachmentNotificationList = CopyOnWriteArrayList<AttachmentNotification>()
private var mActionTaskBinder = ActionTaskBinder()
private var mActionTaskListeners = LinkedList<ActionTaskListener>()

View File

@@ -60,6 +60,15 @@ object ParcelableUtil {
}
}
// For writing map with string key and Int value to a Parcel
fun writeStringIntMap(parcel: Parcel, map: LinkedHashMap<String, Int>) {
parcel.writeInt(map.size)
for ((key, value) in map) {
parcel.writeString(key)
parcel.writeInt(value)
}
}
// For reading map with string key from a Parcel
fun <V : Parcelable> readStringParcelableMap(
parcel: Parcel, vClass: Class<V>): LinkedHashMap<String, V> {
@@ -74,6 +83,19 @@ object ParcelableUtil {
return map
}
// For reading map with string key and Int value from a Parcel
fun readStringIntMap(parcel: Parcel): LinkedHashMap<String, Int> {
val size = parcel.readInt()
val map = LinkedHashMap<String, Int>(size)
for (i in 0 until size) {
val key: String? = parcel.readString()
val value: Int? = parcel.readInt()
if (key != null && value != null)
map[key] = value
}
return map
}
// For writing map with string key and string value to a Parcel
fun writeStringParcelableMap(dest: Parcel, map: LinkedHashMap<String, String>) {