Better custom fields implementation, fix references

This commit is contained in:
J-Jamet
2019-09-06 12:39:27 +02:00
parent 74c08340a6
commit 7c33c9ec02
8 changed files with 67 additions and 247 deletions

View File

@@ -212,10 +212,12 @@ class EntryActivity : LockingHideActivity() {
entryContentsView?.assignComment(entry.notes)
// Assign custom fields
if (entry.allowExtraFields()) {
if (entry.allowCustomFields()) {
entryContentsView?.clearExtraFields()
entry.fields.doActionToAllCustomProtectedField { label, value ->
for (element in entry.customFields.entries) {
val label = element.key
val value = element.value
val allowCopyProtectedField = !value.isProtected || allowCopyPasswordAndProtectedFields
if (allowCopyProtectedField) {

View File

@@ -152,7 +152,7 @@ class EntryEditActivity : LockingHideActivity(),
saveView = findViewById(R.id.entry_edit_save)
saveView?.setOnClickListener { saveEntry() }
entryEditContentsView?.allowCustomField(mNewEntry?.allowExtraFields() == true) { addNewCustomField() }
entryEditContentsView?.allowCustomField(mNewEntry?.allowCustomFields() == true) { addNewCustomField() }
// Verify the education views
entryEditActivityEducation = EntryEditActivityEducation(this)
@@ -172,8 +172,8 @@ class EntryEditActivity : LockingHideActivity(),
url = newEntry.url
password = newEntry.password
notes = newEntry.notes
newEntry.fields.doActionToAllCustomProtectedField { key, value ->
addNewCustomField(key, value)
for (entry in newEntry.customFields.entries) {
addNewCustomField(entry.key, entry.value)
}
}
}
@@ -307,7 +307,7 @@ class EntryEditActivity : LockingHideActivity(),
)
if (!generatePasswordEducationPerformed) {
// entryNewFieldEducationPerformed
mNewEntry != null && mNewEntry!!.allowExtraFields() && !mNewEntry!!.containsCustomFields()
mNewEntry != null && mNewEntry!!.allowCustomFields() && mNewEntry!!.customFields.isEmpty()
&& addNewFieldView != null && addNewFieldView.visibility == View.VISIBLE
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
addNewFieldView,

View File

@@ -24,8 +24,8 @@ class EntryCursorV4 : EntryCursor<PwEntryV4>() {
entry.notes
))
entry.fields.doActionToAllCustomProtectedField { key, value ->
extraFieldCursor.addExtraField(entryId, key, value)
for (element in entry.customFields.entries) {
extraFieldCursor.addExtraField(entryId, element.key, element.value)
}
entryId++

View File

@@ -227,42 +227,18 @@ class EntryVersioned : NodeVersioned, PwEntryInterface<GroupVersioned> {
}
/**
* Retrieve extra fields to show, key is the label, value is the value of field
* Retrieve custom fields to show, key is the label, value is the value of field (protected or not)
* @return Map of label/value
*/
val fields: ExtraFields
get() = pwEntryV4?.fields ?: ExtraFields()
val customFields: HashMap<String, ProtectedString>
get() = pwEntryV4?.customFields ?: HashMap()
/**
* To redefine if version of entry allow extra field,
* @return true if entry allows extra field
* To redefine if version of entry allow custom field,
* @return true if entry allows custom field
*/
fun allowExtraFields(): Boolean {
return pwEntryV4?.allowExtraFields() ?: false
}
/**
* If entry contains extra fields
* @return true if there is extra fields
*/
fun containsCustomFields(): Boolean {
return pwEntryV4?.containsCustomFields() ?: false
}
/**
* If entry contains extra fields that are protected
* @return true if there is extra fields protected
*/
fun containsCustomFieldsProtected(): Boolean {
return pwEntryV4?.containsCustomFieldsProtected() ?: false
}
/**
* If entry contains extra fields that are not protected
* @return true if there is extra fields not protected
*/
fun containsCustomFieldsNotProtected(): Boolean {
return pwEntryV4?.containsCustomFieldsNotProtected() ?: false
fun allowCustomFields(): Boolean {
return pwEntryV4?.allowCustomFields() ?: false
}
/**
@@ -274,13 +250,6 @@ class EntryVersioned : NodeVersioned, PwEntryInterface<GroupVersioned> {
pwEntryV4?.addExtraField(label, value)
}
/**
* Delete all custom fields
*/
fun removeAllCustomFields() {
pwEntryV4?.removeAllCustomFields()
}
fun startToManageFieldReferences(db: PwDatabaseV4) {
pwEntryV4?.startToManageFieldReferences(db)
}
@@ -327,11 +296,9 @@ class EntryVersioned : NodeVersioned, PwEntryInterface<GroupVersioned> {
entryInfo.password = password
entryInfo.url = url
entryInfo.notes = notes
if (containsCustomFields()) {
fields.doActionToAllCustomProtectedField { key, value ->
for (entry in customFields.entries) {
entryInfo.customFields.add(
Field(key, value))
}
Field(entry.key, entry.value))
}
if (!raw)
database?.stopManageEntry(this)

View File

@@ -1,152 +0,0 @@
/*
* Copyright 2018 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 <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.database.element
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.MemUtil
import java.util.HashMap
import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_TITLE
import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_USERNAME
import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_PASSWORD
import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_URL
import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_NOTES
class ExtraFields : Parcelable {
private var fields: MutableMap<String, ProtectedString> = HashMap()
/**
* @return list of standard and customized fields
*/
val listOfAllFields: Map<String, ProtectedString>
get() = fields
private val customProtectedFields: Map<String, ProtectedString>
get() {
val protectedFields = HashMap<String, ProtectedString>()
if (fields.isNotEmpty()) {
for ((key, value) in fields) {
if (isNotStandardField(key)) {
protectedFields[key] = value
}
}
}
return protectedFields
}
constructor()
constructor(extraFields: ExtraFields) : this() {
for ((key, value) in extraFields.fields) {
fields[key] = ProtectedString(value)
}
}
constructor(parcel: Parcel) {
fields = MemUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
MemUtil.writeStringParcelableMap(dest, flags, fields)
}
fun containsCustomFields(): Boolean {
return customProtectedFields.keys.isNotEmpty()
}
fun containsCustomFieldsProtected(): Boolean {
for ((_, value) in customProtectedFields) {
if (value.isProtected)
return true
}
return false
}
fun containsCustomFieldsNotProtected(): Boolean {
for ((_, value) in customProtectedFields) {
if (!value.isProtected)
return true
}
return false
}
fun getProtectedStringValue(key: String): String {
val value = fields[key] ?: return ""
return value.toString()
}
fun putProtectedString(key: String, protectedString: ProtectedString) {
fields[key] = protectedString
}
fun putProtectedString(key: String, value: String, protect: Boolean) {
val ps = ProtectedString(protect, value)
fields[key] = ps
}
fun doActionToAllCustomProtectedField(actionProtected: (key: String, value: ProtectedString)-> Unit) {
for ((key, value) in customProtectedFields) {
actionProtected.invoke(key, value)
}
}
interface ActionProtected {
fun doAction(key: String, value: ProtectedString)
}
fun removeAllCustomFields() {
val iterator = fields.entries.iterator()
while (iterator.hasNext()) {
val pair = iterator.next()
if (isNotStandardField(pair.key)) {
iterator.remove()
}
}
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<ExtraFields> = object : Parcelable.Creator<ExtraFields> {
override fun createFromParcel(parcel: Parcel): ExtraFields {
return ExtraFields(parcel)
}
override fun newArray(size: Int): Array<ExtraFields?> {
return arrayOfNulls(size)
}
}
private fun isNotStandardField(key: String): Boolean {
return (key != STR_TITLE && key != STR_USERNAME
&& key != STR_PASSWORD && key != STR_URL
&& key != STR_NOTES)
}
}
}

View File

@@ -48,8 +48,7 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
}
var iconCustom = PwIconCustom.UNKNOWN_ICON
private var customData = HashMap<String, String>()
var fields = ExtraFields()
private set
var fields = HashMap<String, ProtectedString>()
val binaries = HashMap<String, ProtectedBinary>()
var foregroundColor = ""
var backgroundColor = ""
@@ -63,9 +62,9 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
get() {
var size = FIXED_LENGTH_SIZE
for ((key, value) in fields.listOfAllFields) {
size += key.length.toLong()
size += value.length().toLong()
for (entry in fields.entries) {
size += entry.key.length.toLong()
size += entry.value.length().toLong()
}
for ((key, value) in binaries) {
@@ -96,7 +95,7 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
usageCount = parcel.readLong()
locationChanged = parcel.readParcelable(PwDate::class.java.classLoader)
customData = MemUtil.readStringParcelableMap(parcel)
fields = parcel.readParcelable(ExtraFields::class.java.classLoader)
fields = MemUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
// TODO binaries = MemUtil.readStringParcelableMap(parcel, ProtectedBinary.class);
foregroundColor = parcel.readString()
backgroundColor = parcel.readString()
@@ -114,7 +113,7 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
dest.writeLong(usageCount)
dest.writeParcelable(locationChanged, flags)
MemUtil.writeStringParcelableMap(dest, customData)
dest.writeParcelable(fields, flags)
MemUtil.writeStringParcelableMap(dest, flags, fields)
// TODO MemUtil.writeStringParcelableMap(dest, flags, binaries);
dest.writeString(foregroundColor)
dest.writeString(backgroundColor)
@@ -137,13 +136,11 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
locationChanged = PwDate(source.locationChanged)
// Add all custom elements in map
customData.clear()
for ((key, value) in source.customData) {
customData[key] = value
}
fields = ExtraFields(source.fields)
for ((key, value) in source.binaries) {
binaries[key] = ProtectedBinary(value)
}
customData.putAll(source.customData)
fields.clear()
fields.putAll(source.fields)
binaries.clear()
binaries.putAll(source.binaries)
foregroundColor = source.foregroundColor
backgroundColor = source.backgroundColor
overrideURL = source.overrideURL
@@ -188,17 +185,18 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
* @return
*/
private fun decodeRefKey(decodeRef: Boolean, key: String): String {
val text = fields.getProtectedStringValue(key)
return fields[key]?.toString()?.let { text ->
return if (decodeRef) {
if (mDatabase == null) text else SprEngineV4().compile(text, this, mDatabase!!)
} else text
} ?: ""
}
override var title: String
get() = decodeRefKey(mDecodeRef, STR_TITLE)
set(value) {
val protect = mDatabase != null && mDatabase!!.memoryProtection.protectTitle
fields.putProtectedString(STR_TITLE, value, protect)
fields[STR_TITLE] = ProtectedString(protect, value)
}
override val type: Type
@@ -208,28 +206,28 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
get() = decodeRefKey(mDecodeRef, STR_USERNAME)
set(value) {
val protect = mDatabase != null && mDatabase!!.memoryProtection.protectUserName
fields.putProtectedString(STR_USERNAME, value, protect)
fields[STR_USERNAME] = ProtectedString(protect, value)
}
override var password: String
get() = decodeRefKey(mDecodeRef, STR_PASSWORD)
set(value) {
val protect = mDatabase != null && mDatabase!!.memoryProtection.protectPassword
fields.putProtectedString(STR_PASSWORD, value, protect)
fields[STR_PASSWORD] = ProtectedString(protect, value)
}
override var url
get() = decodeRefKey(mDecodeRef, STR_URL)
set(value) {
val protect = mDatabase != null && mDatabase!!.memoryProtection.protectUrl
fields.putProtectedString(STR_URL, value, protect)
fields[STR_URL] = ProtectedString(protect, value)
}
override var notes: String
get() = decodeRefKey(mDecodeRef, STR_NOTES)
set(value) {
val protect = mDatabase != null && mDatabase!!.memoryProtection.protectNotes
fields.putProtectedString(STR_NOTES, value, protect)
fields[STR_NOTES] = ProtectedString(protect, value)
}
override var usageCount: Long = 0
@@ -240,28 +238,33 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
locationChanged = PwDate()
}
fun allowExtraFields(): Boolean {
private fun isStandardField(key: String): Boolean {
return (key == STR_TITLE
|| key == STR_USERNAME
|| key == STR_PASSWORD
|| key == STR_URL
|| key == STR_NOTES)
}
var customFields = HashMap<String, ProtectedString>()
get() {
field.clear()
for (entry in fields.entries) {
val key = entry.key
val value = entry.value
if (!isStandardField(entry.key)) {
field[key] = ProtectedString(value.isProtected, decodeRefKey(mDecodeRef, key))
}
}
return field
}
fun allowCustomFields(): Boolean {
return true
}
fun containsCustomFields(): Boolean {
return fields.containsCustomFields()
}
fun containsCustomFieldsProtected(): Boolean {
return fields.containsCustomFieldsProtected()
}
fun containsCustomFieldsNotProtected(): Boolean {
return fields.containsCustomFieldsNotProtected()
}
fun addExtraField(label: String, value: ProtectedString) {
fields.putProtectedString(label, value)
}
fun removeAllCustomFields() {
fields.removeAllCustomFields()
fields[label] = value
}
fun putProtectedBinary(key: String, value: ProtectedBinary) {

View File

@@ -349,7 +349,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
writeList(PwDatabaseV4XML.ElemTimes, entry)
writeList(entry.fields.listOfAllFields, true)
writeList(entry.fields, true)
writeList(entry.binaries)
writeList(PwDatabaseV4XML.ElemAutoType, entry.autoType)

View File

@@ -33,13 +33,13 @@ class EntrySearchStringIteratorV4 : EntrySearchStringIterator {
constructor(entry: PwEntryV4) {
this.mSearchParametersV4 = SearchParametersV4()
mSetIterator = entry.fields.listOfAllFields.entries.iterator()
mSetIterator = entry.fields.entries.iterator()
advance()
}
constructor(entry: PwEntryV4, searchParametersV4: SearchParametersV4) {
this.mSearchParametersV4 = searchParametersV4
mSetIterator = entry.fields.listOfAllFields.entries.iterator()
mSetIterator = entry.fields.entries.iterator()
advance()
}