mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'develop' into feature/Templates
This commit is contained in:
@@ -1,4 +1,8 @@
|
|||||||
KeePassDX(3.0.0)
|
KeePassDX(3.0.0)
|
||||||
|
*
|
||||||
|
|
||||||
|
KeePassDX(2.10.0)
|
||||||
|
* Manage new database format 4.1 #956
|
||||||
* Fix show button consistency #980
|
* Fix show button consistency #980
|
||||||
* Fix persistent notification #979
|
* Fix persistent notification #979
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ android {
|
|||||||
applicationId "com.kunzisoft.keepass"
|
applicationId "com.kunzisoft.keepass"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode = 76
|
versionCode = 77
|
||||||
versionName = "3.0.0"
|
versionName = "3.0.0"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImageDraw
|
import com.kunzisoft.keepass.database.element.icon.IconImageDraw
|
||||||
@@ -95,6 +96,12 @@ class IconPickerAdapter<I: IconImageDraw>(val context: Context, private val tint
|
|||||||
override fun onBindViewHolder(holder: CustomIconViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: CustomIconViewHolder, position: Int) {
|
||||||
val icon = iconList[position]
|
val icon = iconList[position]
|
||||||
iconDrawableFactory?.assignDatabaseIcon(holder.iconImageView, icon, tintIcon)
|
iconDrawableFactory?.assignDatabaseIcon(holder.iconImageView, icon, tintIcon)
|
||||||
|
icon.getIconImageToDraw().custom.name.let { iconName ->
|
||||||
|
holder.iconTextView.apply {
|
||||||
|
text = iconName
|
||||||
|
visibility = if (iconName.isNotEmpty()) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
holder.iconContainerView.isSelected = icon.selected
|
holder.iconContainerView.isSelected = icon.selected
|
||||||
holder.itemView.setOnClickListener {
|
holder.itemView.setOnClickListener {
|
||||||
iconPickerListener?.onIconClickListener(icon)
|
iconPickerListener?.onIconClickListener(icon)
|
||||||
@@ -117,5 +124,6 @@ class IconPickerAdapter<I: IconImageDraw>(val context: Context, private val tint
|
|||||||
inner class CustomIconViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
inner class CustomIconViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
var iconContainerView: ViewGroup = itemView.findViewById(R.id.icon_container)
|
var iconContainerView: ViewGroup = itemView.findViewById(R.id.icon_container)
|
||||||
var iconImageView: ImageView = itemView.findViewById(R.id.icon_image)
|
var iconImageView: ImageView = itemView.findViewById(R.id.icon_image)
|
||||||
|
var iconTextView: TextView = itemView.findViewById(R.id.icon_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,8 +124,10 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
mAdvancedUnlockEnabled = PreferencesUtil.isAdvancedUnlockEnable(requireContext())
|
context?.let {
|
||||||
mAutoOpenPromptEnabled = PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(requireContext())
|
mAdvancedUnlockEnabled = PreferencesUtil.isAdvancedUnlockEnable(it)
|
||||||
|
mAutoOpenPromptEnabled = PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(it)
|
||||||
|
}
|
||||||
keepConnection = false
|
keepConnection = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,34 +177,36 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU
|
|||||||
* Check unlock availability and change the current mode depending of device's state
|
* Check unlock availability and change the current mode depending of device's state
|
||||||
*/
|
*/
|
||||||
fun checkUnlockAvailability() {
|
fun checkUnlockAvailability() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
context?.let { context ->
|
||||||
allowOpenBiometricPrompt = true
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (PreferencesUtil.isBiometricUnlockEnable(requireContext())) {
|
allowOpenBiometricPrompt = true
|
||||||
mAdvancedUnlockInfoView?.setIconResource(R.drawable.fingerprint)
|
if (PreferencesUtil.isBiometricUnlockEnable(context)) {
|
||||||
|
mAdvancedUnlockInfoView?.setIconResource(R.drawable.fingerprint)
|
||||||
|
|
||||||
// biometric not supported (by API level or hardware) so keep option hidden
|
// biometric not supported (by API level or hardware) so keep option hidden
|
||||||
// or manually disable
|
// or manually disable
|
||||||
val biometricCanAuthenticate = AdvancedUnlockManager.canAuthenticate(requireContext())
|
val biometricCanAuthenticate = AdvancedUnlockManager.canAuthenticate(context)
|
||||||
if (!PreferencesUtil.isAdvancedUnlockEnable(requireContext())
|
if (!PreferencesUtil.isAdvancedUnlockEnable(context)
|
||||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
|
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
|
||||||
toggleMode(Mode.BIOMETRIC_UNAVAILABLE)
|
toggleMode(Mode.BIOMETRIC_UNAVAILABLE)
|
||||||
} else if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED) {
|
} else if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED) {
|
||||||
toggleMode(Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED)
|
toggleMode(Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED)
|
||||||
} else {
|
|
||||||
// biometric is available but not configured, show icon but in disabled state with some information
|
|
||||||
if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
|
|
||||||
toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
|
|
||||||
} else {
|
} else {
|
||||||
selectMode()
|
// biometric is available but not configured, show icon but in disabled state with some information
|
||||||
|
if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
|
||||||
|
toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
|
||||||
|
} else {
|
||||||
|
selectMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (PreferencesUtil.isDeviceCredentialUnlockEnable(context)) {
|
||||||
|
mAdvancedUnlockInfoView?.setIconResource(R.drawable.bolt)
|
||||||
|
if (AdvancedUnlockManager.isDeviceSecure(context)) {
|
||||||
|
selectMode()
|
||||||
|
} else {
|
||||||
|
toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (PreferencesUtil.isDeviceCredentialUnlockEnable(requireContext())) {
|
|
||||||
mAdvancedUnlockInfoView?.setIconResource(R.drawable.bolt)
|
|
||||||
if (AdvancedUnlockManager.isDeviceSecure(requireContext())) {
|
|
||||||
selectMode()
|
|
||||||
} else {
|
|
||||||
toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +264,7 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU
|
|||||||
private fun openBiometricSetting() {
|
private fun openBiometricSetting() {
|
||||||
mAdvancedUnlockInfoView?.setIconViewClickListener(false) {
|
mAdvancedUnlockInfoView?.setIconViewClickListener(false) {
|
||||||
// ACTION_SECURITY_SETTINGS does not contain fingerprint enrollment on some devices...
|
// ACTION_SECURITY_SETTINGS does not contain fingerprint enrollment on some devices...
|
||||||
requireContext().startActivity(Intent(Settings.ACTION_SETTINGS))
|
context?.startActivity(Intent(Settings.ACTION_SETTINGS))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,9 +299,11 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU
|
|||||||
setAdvancedUnlockedTitleView(R.string.no_credentials_stored)
|
setAdvancedUnlockedTitleView(R.string.no_credentials_stored)
|
||||||
setAdvancedUnlockedMessageView("")
|
setAdvancedUnlockedMessageView("")
|
||||||
|
|
||||||
mAdvancedUnlockInfoView?.setIconViewClickListener(false) {
|
context?.let { context ->
|
||||||
onAuthenticationError(BiometricPrompt.ERROR_UNABLE_TO_PROCESS,
|
mAdvancedUnlockInfoView?.setIconViewClickListener(false) {
|
||||||
requireContext().getString(R.string.credential_before_click_advanced_unlock_button))
|
onAuthenticationError(BiometricPrompt.ERROR_UNABLE_TO_PROCESS,
|
||||||
|
context.getString(R.string.credential_before_click_advanced_unlock_button))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,41 +31,47 @@ class DeleteNodesRunnable(context: Context,
|
|||||||
afterActionNodesFinish: AfterActionNodesFinish)
|
afterActionNodesFinish: AfterActionNodesFinish)
|
||||||
: ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
|
: ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
|
||||||
|
|
||||||
private var mParent: Group? = null
|
private var mOldParent: Group? = null
|
||||||
private var mCanRecycle: Boolean = false
|
private var mCanRecycle: Boolean = false
|
||||||
|
|
||||||
private var mNodesToDeleteBackup = ArrayList<Node>()
|
private var mNodesToDeleteBackup = ArrayList<Node>()
|
||||||
|
|
||||||
override fun nodeAction() {
|
override fun nodeAction() {
|
||||||
|
|
||||||
foreachNode@ for(currentNode in mNodesToDelete) {
|
foreachNode@ for(nodeToDelete in mNodesToDelete) {
|
||||||
mParent = currentNode.parent
|
mOldParent = nodeToDelete.parent
|
||||||
mParent?.touch(modified = false, touchParents = true)
|
mOldParent?.touch(modified = false, touchParents = true)
|
||||||
|
|
||||||
when (currentNode.type) {
|
when (nodeToDelete.type) {
|
||||||
Type.GROUP -> {
|
Type.GROUP -> {
|
||||||
|
val groupToDelete = nodeToDelete as Group
|
||||||
// Create a copy to keep the old ref and remove it visually
|
// Create a copy to keep the old ref and remove it visually
|
||||||
mNodesToDeleteBackup.add(Group(currentNode as Group))
|
mNodesToDeleteBackup.add(Group(groupToDelete))
|
||||||
// Remove Node from parent
|
// Remove Node from parent
|
||||||
mCanRecycle = database.canRecycle(currentNode)
|
mCanRecycle = database.canRecycle(groupToDelete)
|
||||||
if (mCanRecycle) {
|
if (mCanRecycle) {
|
||||||
database.recycle(currentNode, context.resources)
|
groupToDelete.touch(modified = false, touchParents = true)
|
||||||
|
database.recycle(groupToDelete, context.resources)
|
||||||
|
groupToDelete.setPreviousParentGroup(mOldParent)
|
||||||
} else {
|
} else {
|
||||||
database.deleteGroup(currentNode)
|
database.deleteGroup(groupToDelete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type.ENTRY -> {
|
Type.ENTRY -> {
|
||||||
|
val entryToDelete = nodeToDelete as Entry
|
||||||
// Create a copy to keep the old ref and remove it visually
|
// Create a copy to keep the old ref and remove it visually
|
||||||
mNodesToDeleteBackup.add(Entry(currentNode as Entry))
|
mNodesToDeleteBackup.add(Entry(entryToDelete))
|
||||||
// Remove Node from parent
|
// Remove Node from parent
|
||||||
mCanRecycle = database.canRecycle(currentNode)
|
mCanRecycle = database.canRecycle(entryToDelete)
|
||||||
if (mCanRecycle) {
|
if (mCanRecycle) {
|
||||||
database.recycle(currentNode, context.resources)
|
entryToDelete.touch(modified = false, touchParents = true)
|
||||||
|
database.recycle(entryToDelete, context.resources)
|
||||||
|
entryToDelete.setPreviousParentGroup(mOldParent)
|
||||||
} else {
|
} else {
|
||||||
database.deleteEntry(currentNode)
|
database.deleteEntry(entryToDelete)
|
||||||
}
|
}
|
||||||
// Remove the oldest attachments
|
// Remove the oldest attachments
|
||||||
currentNode.getAttachments(database.attachmentPool).forEach {
|
entryToDelete.getAttachments(database.attachmentPool).forEach {
|
||||||
database.removeAttachmentIfNotUsed(it)
|
database.removeAttachmentIfNotUsed(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +82,7 @@ class DeleteNodesRunnable(context: Context,
|
|||||||
override fun nodeFinish(): ActionNodesValues {
|
override fun nodeFinish(): ActionNodesValues {
|
||||||
if (!result.isSuccess) {
|
if (!result.isSuccess) {
|
||||||
if (mCanRecycle) {
|
if (mCanRecycle) {
|
||||||
mParent?.let {
|
mOldParent?.let {
|
||||||
mNodesToDeleteBackup.forEach { backupNode ->
|
mNodesToDeleteBackup.forEach { backupNode ->
|
||||||
when (backupNode.type) {
|
when (backupNode.type) {
|
||||||
Type.GROUP -> {
|
Type.GROUP -> {
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ class MoveNodesRunnable constructor(
|
|||||||
// and if not in the current group
|
// and if not in the current group
|
||||||
&& groupToMove != mNewParent
|
&& groupToMove != mNewParent
|
||||||
&& !mNewParent.isContainedIn(groupToMove)) {
|
&& !mNewParent.isContainedIn(groupToMove)) {
|
||||||
nodeToMove.touch(modified = true, touchParents = true)
|
groupToMove.touch(modified = true, touchParents = true)
|
||||||
database.moveGroupTo(groupToMove, mNewParent)
|
database.moveGroupTo(groupToMove, mNewParent)
|
||||||
|
groupToMove.setPreviousParentGroup(mOldParent)
|
||||||
} else {
|
} else {
|
||||||
// Only finish thread
|
// Only finish thread
|
||||||
setError(MoveGroupDatabaseException())
|
setError(MoveGroupDatabaseException())
|
||||||
@@ -66,8 +67,9 @@ class MoveNodesRunnable constructor(
|
|||||||
if (mOldParent != mNewParent
|
if (mOldParent != mNewParent
|
||||||
// and root can contains entry
|
// and root can contains entry
|
||||||
&& (mNewParent != database.rootGroup || database.rootCanContainsEntry())) {
|
&& (mNewParent != database.rootGroup || database.rootCanContainsEntry())) {
|
||||||
nodeToMove.touch(modified = true, touchParents = true)
|
entryToMove.touch(modified = true, touchParents = true)
|
||||||
database.moveEntryTo(entryToMove, mNewParent)
|
database.moveEntryTo(entryToMove, mNewParent)
|
||||||
|
entryToMove.setPreviousParentGroup(mOldParent)
|
||||||
} else {
|
} else {
|
||||||
// Only finish thread
|
// Only finish thread
|
||||||
setError(MoveEntryDatabaseException())
|
setError(MoveEntryDatabaseException())
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.kunzisoft.keepass.database.element
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import com.kunzisoft.keepass.utils.ParcelableUtil
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class CustomData : Parcelable {
|
||||||
|
|
||||||
|
private val mCustomDataItems = HashMap<String, CustomDataItem>()
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
constructor(toCopy: CustomData) {
|
||||||
|
mCustomDataItems.clear()
|
||||||
|
mCustomDataItems.putAll(toCopy.mCustomDataItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) {
|
||||||
|
ParcelableUtil.readStringParcelableMap(parcel, CustomData::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(key: String): CustomDataItem? {
|
||||||
|
return mCustomDataItems[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(customDataItem: CustomDataItem) {
|
||||||
|
mCustomDataItems[customDataItem.key] = customDataItem
|
||||||
|
}
|
||||||
|
|
||||||
|
fun containsItemWithValue(value: String): Boolean {
|
||||||
|
return mCustomDataItems.any { mapEntry -> mapEntry.value.value.equals(value, true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun containsItemWithLastModificationTime(): Boolean {
|
||||||
|
return mCustomDataItems.any { mapEntry -> mapEntry.value.lastModificationTime != null }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isNotEmpty(): Boolean {
|
||||||
|
return mCustomDataItems.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doForEachItems(action: (CustomDataItem) -> Unit) {
|
||||||
|
for ((_, value) in mCustomDataItems) {
|
||||||
|
action.invoke(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
ParcelableUtil.writeStringParcelableMap(parcel, flags, mCustomDataItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<CustomData> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): CustomData {
|
||||||
|
return CustomData(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<CustomData?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.kunzisoft.keepass.database.element
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
|
||||||
|
class CustomDataItem : Parcelable {
|
||||||
|
|
||||||
|
val key: String
|
||||||
|
var value: String
|
||||||
|
var lastModificationTime: DateInstant? = null
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) {
|
||||||
|
key = parcel.readString() ?: ""
|
||||||
|
value = parcel.readString() ?: ""
|
||||||
|
lastModificationTime = parcel.readParcelable(DateInstant::class.java.classLoader)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(key: String, value: String, lastModificationTime: DateInstant? = null) {
|
||||||
|
this.key = key
|
||||||
|
this.value = value
|
||||||
|
this.lastModificationTime = lastModificationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeString(key)
|
||||||
|
parcel.writeString(value)
|
||||||
|
parcel.writeParcelable(lastModificationTime, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<CustomDataItem> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): CustomDataItem {
|
||||||
|
return CustomDataItem(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<CustomDataItem?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
|||||||
import com.kunzisoft.keepass.database.exception.*
|
import com.kunzisoft.keepass.database.exception.*
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
|
||||||
import com.kunzisoft.keepass.database.file.input.DatabaseInputKDB
|
import com.kunzisoft.keepass.database.file.input.DatabaseInputKDB
|
||||||
import com.kunzisoft.keepass.database.file.input.DatabaseInputKDBX
|
import com.kunzisoft.keepass.database.file.input.DatabaseInputKDBX
|
||||||
import com.kunzisoft.keepass.database.file.output.DatabaseOutputKDB
|
import com.kunzisoft.keepass.database.file.output.DatabaseOutputKDB
|
||||||
@@ -222,7 +222,7 @@ class Database {
|
|||||||
// Default compression not necessary if stored in header
|
// Default compression not necessary if stored in header
|
||||||
mDatabaseKDBX?.let {
|
mDatabaseKDBX?.let {
|
||||||
return it.compressionAlgorithm == CompressionAlgorithm.GZip
|
return it.compressionAlgorithm == CompressionAlgorithm.GZip
|
||||||
&& it.kdbxVersion.isBefore(FILE_VERSION_32_4)
|
&& it.kdbxVersion.isBefore(FILE_VERSION_40)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class DateInstant : Parcelable {
|
|||||||
jDate = Date()
|
jDate = Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected constructor(parcel: Parcel) {
|
constructor(parcel: Parcel) {
|
||||||
jDate = parcel.readSerializable() as Date
|
jDate = parcel.readSerializable() as Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,30 +19,37 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.element
|
package com.kunzisoft.keepass.database.element
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.ParcelUuid
|
||||||
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
import java.util.Date
|
import java.util.*
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
class DeletedObject {
|
class DeletedObject : Parcelable {
|
||||||
|
|
||||||
var uuid: UUID = DatabaseVersioned.UUID_ZERO
|
var uuid: UUID = DatabaseVersioned.UUID_ZERO
|
||||||
private var mDeletionTime: Date? = null
|
private var mDeletionTime: DateInstant? = null
|
||||||
|
|
||||||
fun getDeletionTime(): Date {
|
constructor()
|
||||||
|
|
||||||
|
constructor(uuid: UUID, deletionTime: DateInstant = DateInstant()) {
|
||||||
|
this.uuid = uuid
|
||||||
|
this.mDeletionTime = deletionTime
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) {
|
||||||
|
uuid = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: DatabaseVersioned.UUID_ZERO
|
||||||
|
mDeletionTime = parcel.readParcelable(DateInstant::class.java.classLoader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDeletionTime(): DateInstant {
|
||||||
if (mDeletionTime == null) {
|
if (mDeletionTime == null) {
|
||||||
mDeletionTime = Date(System.currentTimeMillis())
|
mDeletionTime = DateInstant(System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
return mDeletionTime!!
|
return mDeletionTime!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDeletionTime(deletionTime: Date) {
|
fun setDeletionTime(deletionTime: DateInstant) {
|
||||||
this.mDeletionTime = deletionTime
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
|
|
||||||
constructor(uuid: UUID, deletionTime: Date = Date()) {
|
|
||||||
this.uuid = uuid
|
|
||||||
this.mDeletionTime = deletionTime
|
this.mDeletionTime = deletionTime
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,4 +66,23 @@ class DeletedObject {
|
|||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return uuid.hashCode()
|
return uuid.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeParcelable(ParcelUuid(uuid), flags)
|
||||||
|
parcel.writeParcelable(mDeletionTime, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<DeletedObject> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): DeletedObject {
|
||||||
|
return DeletedObject(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<DeletedObject?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.os.Parcel
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||||
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
import com.kunzisoft.keepass.database.element.entry.EntryKDB
|
import com.kunzisoft.keepass.database.element.entry.EntryKDB
|
||||||
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
||||||
import com.kunzisoft.keepass.database.element.entry.EntryVersionedInterface
|
import com.kunzisoft.keepass.database.element.entry.EntryVersionedInterface
|
||||||
@@ -114,6 +115,20 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
|||||||
entryKDBX?.icon = value
|
entryKDBX?.icon = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tags: Tags
|
||||||
|
get() = entryKDBX?.tags ?: Tags()
|
||||||
|
set(value) {
|
||||||
|
entryKDBX?.tags = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousParentGroup: UUID = DatabaseVersioned.UUID_ZERO
|
||||||
|
get() = entryKDBX?.previousParentGroup ?: DatabaseVersioned.UUID_ZERO
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun setPreviousParentGroup(previousParent: Group?) {
|
||||||
|
entryKDBX?.previousParentGroup = previousParent?.groupKDBX?.id ?: DatabaseVersioned.UUID_ZERO
|
||||||
|
}
|
||||||
|
|
||||||
override val type: Type
|
override val type: Type
|
||||||
get() = Type.ENTRY
|
get() = Type.ENTRY
|
||||||
|
|
||||||
@@ -373,10 +388,6 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
|||||||
return entryKDBX?.getSize(attachmentPool) ?: 0L
|
return entryKDBX?.getSize(attachmentPool) ?: 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
fun containsCustomData(): Boolean {
|
|
||||||
return entryKDBX?.containsCustomData() ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
------------
|
------------
|
||||||
Converter
|
Converter
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.database.element
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
||||||
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
||||||
import com.kunzisoft.keepass.database.element.group.GroupVersionedInterface
|
import com.kunzisoft.keepass.database.element.group.GroupVersionedInterface
|
||||||
@@ -134,6 +135,20 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
|
|||||||
groupKDBX?.icon = value
|
groupKDBX?.icon = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tags: Tags
|
||||||
|
get() = groupKDBX?.tags ?: Tags()
|
||||||
|
set(value) {
|
||||||
|
groupKDBX?.tags = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousParentGroup: UUID = DatabaseVersioned.UUID_ZERO
|
||||||
|
get() = groupKDBX?.previousParentGroup ?: DatabaseVersioned.UUID_ZERO
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun setPreviousParentGroup(previousParent: Group?) {
|
||||||
|
groupKDBX?.previousParentGroup = previousParent?.groupKDBX?.id ?: DatabaseVersioned.UUID_ZERO
|
||||||
|
}
|
||||||
|
|
||||||
override val type: Type
|
override val type: Type
|
||||||
get() = Type.GROUP
|
get() = Type.GROUP
|
||||||
|
|
||||||
@@ -394,10 +409,6 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
|
|||||||
groupKDBX?.isExpanded = expanded
|
groupKDBX?.isExpanded = expanded
|
||||||
}
|
}
|
||||||
|
|
||||||
fun containsCustomData(): Boolean {
|
|
||||||
return groupKDBX?.containsCustomData() ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
------------
|
------------
|
||||||
Converter
|
Converter
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.kunzisoft.keepass.database.element
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
|
||||||
|
class Tags: Parcelable {
|
||||||
|
|
||||||
|
private val mTags = ArrayList<String>()
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
constructor(values: String): this() {
|
||||||
|
mTags.addAll(values.split(';'))
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) : this() {
|
||||||
|
parcel.readStringList(mTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeStringList(mTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isEmpty(): Boolean {
|
||||||
|
return mTags.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return mTags.joinToString(";")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<Tags> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): Tags {
|
||||||
|
return Tags(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<Tags?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,27 @@
|
|||||||
package com.kunzisoft.keepass.database.element.binary
|
package com.kunzisoft.keepass.database.element.binary
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
|
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CustomIconPool(binaryCache: BinaryCache) : BinaryPool<UUID>(binaryCache) {
|
class CustomIconPool(private val binaryCache: BinaryCache) : BinaryPool<UUID>(binaryCache) {
|
||||||
|
|
||||||
|
private val customIcons = HashMap<UUID, IconImageCustom>()
|
||||||
|
|
||||||
|
fun put(key: UUID? = null,
|
||||||
|
name: String,
|
||||||
|
lastModificationTime: DateInstant?,
|
||||||
|
smallSize: Boolean,
|
||||||
|
result: (IconImageCustom, BinaryData?) -> Unit) {
|
||||||
|
val keyBinary = super.put(key) { uniqueBinaryId ->
|
||||||
|
// Create a byte array for better performance with small data
|
||||||
|
binaryCache.getBinaryData(uniqueBinaryId, smallSize)
|
||||||
|
}
|
||||||
|
val uuid = keyBinary.keys.first()
|
||||||
|
val customIcon = IconImageCustom(uuid, name, lastModificationTime)
|
||||||
|
customIcons[uuid] = customIcon
|
||||||
|
result.invoke(customIcon, keyBinary.binary)
|
||||||
|
}
|
||||||
|
|
||||||
override fun findUnusedKey(): UUID {
|
override fun findUnusedKey(): UUID {
|
||||||
var newUUID = UUID.randomUUID()
|
var newUUID = UUID.randomUUID()
|
||||||
@@ -11,4 +30,14 @@ class CustomIconPool(binaryCache: BinaryCache) : BinaryPool<UUID>(binaryCache) {
|
|||||||
}
|
}
|
||||||
return newUUID
|
return newUUID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun any(predicate: (IconImageCustom)-> Boolean): Boolean {
|
||||||
|
return customIcons.any { predicate(it.value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doForEachCustomIcon(action: (customIcon: IconImageCustom, binary: BinaryData) -> Unit) {
|
||||||
|
doForEachBinary { key, binary ->
|
||||||
|
action.invoke(customIcons[key] ?: IconImageCustom(key), binary)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -157,10 +157,6 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
|||||||
return this.iconsManager.getIcon(iconId)
|
return this.iconsManager.getIcon(iconId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun containsCustomData(): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isInRecycleBin(group: GroupKDB): Boolean {
|
override fun isInRecycleBin(group: GroupKDB): Boolean {
|
||||||
var currentGroup: GroupKDB? = group
|
var currentGroup: GroupKDB? = group
|
||||||
val currentBackupGroup = backupGroup ?: return false
|
val currentBackupGroup = backupGroup ?: return false
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.kunzisoft.keepass.database.crypto.VariantDictionary
|
|||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfParameters
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfParameters
|
||||||
|
import com.kunzisoft.keepass.database.element.CustomData
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.DeletedObject
|
import com.kunzisoft.keepass.database.element.DeletedObject
|
||||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||||
@@ -45,8 +46,9 @@ import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
|||||||
import com.kunzisoft.keepass.database.element.node.NodeVersioned
|
import com.kunzisoft.keepass.database.element.node.NodeVersioned
|
||||||
import com.kunzisoft.keepass.database.element.security.MemoryProtectionConfig
|
import com.kunzisoft.keepass.database.element.security.MemoryProtectionConfig
|
||||||
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_3
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_31
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
|
||||||
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_41
|
||||||
import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
|
import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
|
||||||
import com.kunzisoft.keepass.utils.StringUtil.toHexString
|
import com.kunzisoft.keepass.utils.StringUtil.toHexString
|
||||||
import com.kunzisoft.keepass.utils.UnsignedInt
|
import com.kunzisoft.keepass.utils.UnsignedInt
|
||||||
@@ -102,7 +104,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
*/
|
*/
|
||||||
var isRecycleBinEnabled = true
|
var isRecycleBinEnabled = true
|
||||||
var recycleBinUUID: UUID = UUID_ZERO
|
var recycleBinUUID: UUID = UUID_ZERO
|
||||||
var recycleBinChanged = Date()
|
var recycleBinChanged = DateInstant()
|
||||||
var entryTemplatesGroup = UUID_ZERO
|
var entryTemplatesGroup = UUID_ZERO
|
||||||
var entryTemplatesGroupChanged = DateInstant()
|
var entryTemplatesGroupChanged = DateInstant()
|
||||||
var historyMaxItems = DEFAULT_HISTORY_MAX_ITEMS
|
var historyMaxItems = DEFAULT_HISTORY_MAX_ITEMS
|
||||||
@@ -111,7 +113,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
var lastTopVisibleGroupUUID = UUID_ZERO
|
var lastTopVisibleGroupUUID = UUID_ZERO
|
||||||
var memoryProtection = MemoryProtectionConfig()
|
var memoryProtection = MemoryProtectionConfig()
|
||||||
val deletedObjects = ArrayList<DeletedObject>()
|
val deletedObjects = ArrayList<DeletedObject>()
|
||||||
val customData = HashMap<String, String>()
|
val customData = CustomData()
|
||||||
|
|
||||||
var localizedAppName = "KeePassDX"
|
var localizedAppName = "KeePassDX"
|
||||||
|
|
||||||
@@ -128,7 +130,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
*/
|
*/
|
||||||
constructor(databaseName: String, rootName: String) {
|
constructor(databaseName: String, rootName: String) {
|
||||||
name = databaseName
|
name = databaseName
|
||||||
kdbxVersion = FILE_VERSION_32_3
|
kdbxVersion = FILE_VERSION_31
|
||||||
val group = createGroup().apply {
|
val group = createGroup().apply {
|
||||||
title = rootName
|
title = rootName
|
||||||
icon.standard = getStandardIcon(IconImageStandard.FOLDER_ID)
|
icon.standard = getStandardIcon(IconImageStandard.FOLDER_ID)
|
||||||
@@ -139,8 +141,9 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
override val version: String
|
override val version: String
|
||||||
get() {
|
get() {
|
||||||
val kdbxStringVersion = when(kdbxVersion) {
|
val kdbxStringVersion = when(kdbxVersion) {
|
||||||
FILE_VERSION_32_3 -> "3.1"
|
FILE_VERSION_31 -> "3.1"
|
||||||
FILE_VERSION_32_4 -> "4.0"
|
FILE_VERSION_40 -> "4.0"
|
||||||
|
FILE_VERSION_41 -> "4.1"
|
||||||
else -> "UNKNOWN"
|
else -> "UNKNOWN"
|
||||||
}
|
}
|
||||||
return "KeePass 2 - KDBX$kdbxStringVersion"
|
return "KeePass 2 - KDBX$kdbxStringVersion"
|
||||||
@@ -188,7 +191,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
}
|
}
|
||||||
CompressionAlgorithm.GZip -> {
|
CompressionAlgorithm.GZip -> {
|
||||||
// Only in databaseV3.1, in databaseV4 the header is zipped during the save
|
// Only in databaseV3.1, in databaseV4 the header is zipped during the save
|
||||||
if (kdbxVersion.isBefore(FILE_VERSION_32_4)) {
|
if (kdbxVersion.isBefore(FILE_VERSION_40)) {
|
||||||
compressAllBinaries()
|
compressAllBinaries()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +199,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
}
|
}
|
||||||
CompressionAlgorithm.GZip -> {
|
CompressionAlgorithm.GZip -> {
|
||||||
// In databaseV4 the header is zipped during the save, so not necessary here
|
// In databaseV4 the header is zipped during the save, so not necessary here
|
||||||
if (kdbxVersion.isBefore(FILE_VERSION_32_4)) {
|
if (kdbxVersion.isBefore(FILE_VERSION_40)) {
|
||||||
when (newCompression) {
|
when (newCompression) {
|
||||||
CompressionAlgorithm.None -> {
|
CompressionAlgorithm.None -> {
|
||||||
decompressAllBinaries()
|
decompressAllBinaries()
|
||||||
@@ -314,9 +317,11 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addCustomIcon(customIconId: UUID? = null,
|
fun addCustomIcon(customIconId: UUID? = null,
|
||||||
|
name: String,
|
||||||
|
lastModificationTime: DateInstant?,
|
||||||
smallSize: Boolean,
|
smallSize: Boolean,
|
||||||
result: (IconImageCustom, BinaryData?) -> Unit) {
|
result: (IconImageCustom, BinaryData?) -> Unit) {
|
||||||
iconsManager.addCustomIcon(customIconId, smallSize, result)
|
iconsManager.addCustomIcon(customIconId, name, lastModificationTime, smallSize, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isCustomIconBinaryDuplicate(binary: BinaryData): Boolean {
|
fun isCustomIconBinaryDuplicate(binary: BinaryData): Boolean {
|
||||||
@@ -327,17 +332,12 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
return this.iconsManager.getIcon(iconUuid)
|
return this.iconsManager.getIcon(iconUuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putCustomData(label: String, value: String) {
|
/**
|
||||||
this.customData[label] = value
|
* To perform a search in entry custom data
|
||||||
}
|
*/
|
||||||
|
|
||||||
override fun containsCustomData(): Boolean {
|
|
||||||
return customData.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getEntryByCustomData(customDataValue: String): EntryKDBX? {
|
fun getEntryByCustomData(customDataValue: String): EntryKDBX? {
|
||||||
return entryIndexes.values.find { entry ->
|
return entryIndexes.values.find { entry ->
|
||||||
entry.customData.containsValue(customDataValue)
|
entry.customData.containsItemWithValue(customDataValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,14 +608,14 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
}
|
}
|
||||||
addGroupTo(recycleBinGroup, rootGroup)
|
addGroupTo(recycleBinGroup, rootGroup)
|
||||||
recycleBinUUID = recycleBinGroup.id
|
recycleBinUUID = recycleBinGroup.id
|
||||||
recycleBinChanged = recycleBinGroup.lastModificationTime.date
|
recycleBinChanged = recycleBinGroup.lastModificationTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeRecycleBin() {
|
fun removeRecycleBin() {
|
||||||
if (recycleBin != null) {
|
if (recycleBin != null) {
|
||||||
recycleBinUUID = UUID_ZERO
|
recycleBinUUID = UUID_ZERO
|
||||||
recycleBinChanged = DateInstant().date
|
recycleBinChanged = DateInstant()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -337,8 +337,6 @@ abstract class DatabaseVersioned<
|
|||||||
|
|
||||||
abstract fun getStandardIcon(iconId: Int): IconImageStandard
|
abstract fun getStandardIcon(iconId: Int): IconImageStandard
|
||||||
|
|
||||||
abstract fun containsCustomData(): Boolean
|
|
||||||
|
|
||||||
fun addGroupTo(newGroup: Group, parent: Group?) {
|
fun addGroupTo(newGroup: Group, parent: Group?) {
|
||||||
// Add tree to parent tree
|
// Add tree to parent tree
|
||||||
parent?.addChildGroup(newGroup)
|
parent?.addChildGroup(newGroup)
|
||||||
|
|||||||
@@ -20,12 +20,15 @@
|
|||||||
package com.kunzisoft.keepass.database.element.entry
|
package com.kunzisoft.keepass.database.element.entry
|
||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
|
import android.os.ParcelUuid
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.utils.UnsignedLong
|
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
|
import com.kunzisoft.keepass.database.element.CustomData
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
|
import com.kunzisoft.keepass.database.element.Tags
|
||||||
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||||
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||||
@@ -33,6 +36,7 @@ import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface
|
|||||||
import com.kunzisoft.keepass.database.element.node.Type
|
import com.kunzisoft.keepass.database.element.node.Type
|
||||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
import com.kunzisoft.keepass.utils.ParcelableUtil
|
import com.kunzisoft.keepass.utils.ParcelableUtil
|
||||||
|
import com.kunzisoft.keepass.utils.UnsignedLong
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.LinkedHashMap
|
import kotlin.collections.LinkedHashMap
|
||||||
@@ -45,16 +49,20 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
@Transient
|
@Transient
|
||||||
private var mDecodeRef = false
|
private var mDecodeRef = false
|
||||||
|
|
||||||
var customData = LinkedHashMap<String, String>()
|
override var usageCount = UnsignedLong(0)
|
||||||
|
override var locationChanged = DateInstant()
|
||||||
|
override var customData = CustomData()
|
||||||
var fields = LinkedHashMap<String, ProtectedString>()
|
var fields = LinkedHashMap<String, ProtectedString>()
|
||||||
var binaries = LinkedHashMap<String, Int>() // Map<Label, PoolId>
|
var binaries = LinkedHashMap<String, Int>() // Map<Label, PoolId>
|
||||||
var foregroundColor = ""
|
var foregroundColor = ""
|
||||||
var backgroundColor = ""
|
var backgroundColor = ""
|
||||||
var overrideURL = ""
|
var overrideURL = ""
|
||||||
|
override var tags = Tags()
|
||||||
|
override var previousParentGroup: UUID = DatabaseVersioned.UUID_ZERO
|
||||||
|
var qualityCheck = true
|
||||||
var autoType = AutoType()
|
var autoType = AutoType()
|
||||||
var history = ArrayList<EntryKDBX>()
|
var history = ArrayList<EntryKDBX>()
|
||||||
var additional = ""
|
var additional = ""
|
||||||
var tags = ""
|
|
||||||
|
|
||||||
override var expires: Boolean = false
|
override var expires: Boolean = false
|
||||||
|
|
||||||
@@ -63,17 +71,18 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
constructor(parcel: Parcel) : super(parcel) {
|
constructor(parcel: Parcel) : super(parcel) {
|
||||||
usageCount = UnsignedLong(parcel.readLong())
|
usageCount = UnsignedLong(parcel.readLong())
|
||||||
locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged
|
locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged
|
||||||
customData = ParcelableUtil.readStringParcelableMap(parcel)
|
customData = parcel.readParcelable(CustomData::class.java.classLoader) ?: CustomData()
|
||||||
fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
|
fields = ParcelableUtil.readStringParcelableMap(parcel, ProtectedString::class.java)
|
||||||
binaries = ParcelableUtil.readStringIntMap(parcel)
|
binaries = ParcelableUtil.readStringIntMap(parcel)
|
||||||
foregroundColor = parcel.readString() ?: foregroundColor
|
foregroundColor = parcel.readString() ?: foregroundColor
|
||||||
backgroundColor = parcel.readString() ?: backgroundColor
|
backgroundColor = parcel.readString() ?: backgroundColor
|
||||||
overrideURL = parcel.readString() ?: overrideURL
|
overrideURL = parcel.readString() ?: overrideURL
|
||||||
|
tags = parcel.readParcelable(Tags::class.java.classLoader) ?: tags
|
||||||
|
previousParentGroup = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: DatabaseVersioned.UUID_ZERO
|
||||||
autoType = parcel.readParcelable(AutoType::class.java.classLoader) ?: autoType
|
autoType = parcel.readParcelable(AutoType::class.java.classLoader) ?: autoType
|
||||||
parcel.readTypedList(history, CREATOR)
|
parcel.readTypedList(history, CREATOR)
|
||||||
url = parcel.readString() ?: url
|
url = parcel.readString() ?: url
|
||||||
additional = parcel.readString() ?: additional
|
additional = parcel.readString() ?: additional
|
||||||
tags = parcel.readString() ?: tags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
|
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
|
||||||
@@ -88,17 +97,18 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
super.writeToParcel(dest, flags)
|
super.writeToParcel(dest, flags)
|
||||||
dest.writeLong(usageCount.toKotlinLong())
|
dest.writeLong(usageCount.toKotlinLong())
|
||||||
dest.writeParcelable(locationChanged, flags)
|
dest.writeParcelable(locationChanged, flags)
|
||||||
ParcelableUtil.writeStringParcelableMap(dest, customData)
|
dest.writeParcelable(customData, flags)
|
||||||
ParcelableUtil.writeStringParcelableMap(dest, flags, fields)
|
ParcelableUtil.writeStringParcelableMap(dest, flags, fields)
|
||||||
ParcelableUtil.writeStringIntMap(dest, binaries)
|
ParcelableUtil.writeStringIntMap(dest, binaries)
|
||||||
dest.writeString(foregroundColor)
|
dest.writeString(foregroundColor)
|
||||||
dest.writeString(backgroundColor)
|
dest.writeString(backgroundColor)
|
||||||
dest.writeString(overrideURL)
|
dest.writeString(overrideURL)
|
||||||
|
dest.writeParcelable(tags, flags)
|
||||||
|
dest.writeParcelable(ParcelUuid(previousParentGroup), flags)
|
||||||
dest.writeParcelable(autoType, flags)
|
dest.writeParcelable(autoType, flags)
|
||||||
dest.writeTypedList(history)
|
dest.writeTypedList(history)
|
||||||
dest.writeString(url)
|
dest.writeString(url)
|
||||||
dest.writeString(additional)
|
dest.writeString(additional)
|
||||||
dest.writeString(tags)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,9 +119,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
super.updateWith(source)
|
super.updateWith(source)
|
||||||
usageCount = source.usageCount
|
usageCount = source.usageCount
|
||||||
locationChanged = DateInstant(source.locationChanged)
|
locationChanged = DateInstant(source.locationChanged)
|
||||||
// Add all custom elements in map
|
customData = CustomData(source.customData)
|
||||||
customData.clear()
|
|
||||||
customData.putAll(source.customData)
|
|
||||||
fields.clear()
|
fields.clear()
|
||||||
fields.putAll(source.fields)
|
fields.putAll(source.fields)
|
||||||
binaries.clear()
|
binaries.clear()
|
||||||
@@ -119,13 +127,14 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
foregroundColor = source.foregroundColor
|
foregroundColor = source.foregroundColor
|
||||||
backgroundColor = source.backgroundColor
|
backgroundColor = source.backgroundColor
|
||||||
overrideURL = source.overrideURL
|
overrideURL = source.overrideURL
|
||||||
|
tags = source.tags
|
||||||
|
previousParentGroup = source.previousParentGroup
|
||||||
autoType = AutoType(source.autoType)
|
autoType = AutoType(source.autoType)
|
||||||
history.clear()
|
history.clear()
|
||||||
if (copyHistory)
|
if (copyHistory)
|
||||||
history.addAll(source.history)
|
history.addAll(source.history)
|
||||||
url = source.url
|
url = source.url
|
||||||
additional = source.additional
|
additional = source.additional
|
||||||
tags = source.tags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startToManageFieldReferences(database: DatabaseKDBX) {
|
fun startToManageFieldReferences(database: DatabaseKDBX) {
|
||||||
@@ -218,10 +227,6 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
fields[STR_NOTES] = ProtectedString(protect, value)
|
fields[STR_NOTES] = ProtectedString(protect, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override var usageCount = UnsignedLong(0)
|
|
||||||
|
|
||||||
override var locationChanged = DateInstant()
|
|
||||||
|
|
||||||
fun getSize(attachmentPool: AttachmentPool): Long {
|
fun getSize(attachmentPool: AttachmentPool): Long {
|
||||||
var size = FIXED_LENGTH_SIZE
|
var size = FIXED_LENGTH_SIZE
|
||||||
|
|
||||||
@@ -243,7 +248,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
size += overrideURL.length.toLong()
|
size += overrideURL.length.toLong()
|
||||||
size += tags.length.toLong()
|
size += tags.toString().length
|
||||||
|
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
@@ -322,14 +327,6 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putCustomData(key: String, value: String) {
|
|
||||||
customData[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun containsCustomData(): Boolean {
|
|
||||||
return customData.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addEntryToHistory(entry: EntryKDBX) {
|
fun addEntryToHistory(entry: EntryKDBX) {
|
||||||
history.add(entry)
|
history.add(entry)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,11 @@
|
|||||||
package com.kunzisoft.keepass.database.element.group
|
package com.kunzisoft.keepass.database.element.group
|
||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
|
import android.os.ParcelUuid
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.kunzisoft.keepass.database.element.CustomData
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
|
import com.kunzisoft.keepass.database.element.Tags
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
@@ -33,14 +36,17 @@ import java.util.*
|
|||||||
|
|
||||||
class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInterface {
|
class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInterface {
|
||||||
|
|
||||||
private val customData = HashMap<String, String>()
|
override var usageCount = UnsignedLong(0)
|
||||||
|
override var locationChanged = DateInstant()
|
||||||
|
override var customData = CustomData()
|
||||||
var notes = ""
|
var notes = ""
|
||||||
|
|
||||||
var isExpanded = true
|
var isExpanded = true
|
||||||
var defaultAutoTypeSequence = ""
|
var defaultAutoTypeSequence = ""
|
||||||
var enableAutoType: Boolean? = null
|
var enableAutoType: Boolean? = null
|
||||||
var enableSearching: Boolean? = null
|
var enableSearching: Boolean? = null
|
||||||
var lastTopVisibleEntry: UUID = DatabaseVersioned.UUID_ZERO
|
var lastTopVisibleEntry: UUID = DatabaseVersioned.UUID_ZERO
|
||||||
|
override var tags = Tags()
|
||||||
|
override var previousParentGroup: UUID = DatabaseVersioned.UUID_ZERO
|
||||||
|
|
||||||
override var expires: Boolean = false
|
override var expires: Boolean = false
|
||||||
|
|
||||||
@@ -60,7 +66,7 @@ class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
constructor(parcel: Parcel) : super(parcel) {
|
constructor(parcel: Parcel) : super(parcel) {
|
||||||
usageCount = UnsignedLong(parcel.readLong())
|
usageCount = UnsignedLong(parcel.readLong())
|
||||||
locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged
|
locationChanged = parcel.readParcelable(DateInstant::class.java.classLoader) ?: locationChanged
|
||||||
// TODO customData = ParcelableUtil.readStringParcelableMap(parcel);
|
customData = parcel.readParcelable(CustomData::class.java.classLoader) ?: CustomData()
|
||||||
notes = parcel.readString() ?: notes
|
notes = parcel.readString() ?: notes
|
||||||
isExpanded = parcel.readByte().toInt() != 0
|
isExpanded = parcel.readByte().toInt() != 0
|
||||||
defaultAutoTypeSequence = parcel.readString() ?: defaultAutoTypeSequence
|
defaultAutoTypeSequence = parcel.readString() ?: defaultAutoTypeSequence
|
||||||
@@ -69,6 +75,8 @@ class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
val isSearchingEnabled = parcel.readInt()
|
val isSearchingEnabled = parcel.readInt()
|
||||||
enableSearching = if (isSearchingEnabled == -1) null else isSearchingEnabled == 1
|
enableSearching = if (isSearchingEnabled == -1) null else isSearchingEnabled == 1
|
||||||
lastTopVisibleEntry = parcel.readSerializable() as UUID
|
lastTopVisibleEntry = parcel.readSerializable() as UUID
|
||||||
|
tags = parcel.readParcelable(Tags::class.java.classLoader) ?: tags
|
||||||
|
previousParentGroup = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: DatabaseVersioned.UUID_ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
|
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
|
||||||
@@ -83,13 +91,15 @@ class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
super.writeToParcel(dest, flags)
|
super.writeToParcel(dest, flags)
|
||||||
dest.writeLong(usageCount.toKotlinLong())
|
dest.writeLong(usageCount.toKotlinLong())
|
||||||
dest.writeParcelable(locationChanged, flags)
|
dest.writeParcelable(locationChanged, flags)
|
||||||
// TODO ParcelableUtil.writeStringParcelableMap(dest, customData);
|
dest.writeParcelable(customData, flags)
|
||||||
dest.writeString(notes)
|
dest.writeString(notes)
|
||||||
dest.writeByte((if (isExpanded) 1 else 0).toByte())
|
dest.writeByte((if (isExpanded) 1 else 0).toByte())
|
||||||
dest.writeString(defaultAutoTypeSequence)
|
dest.writeString(defaultAutoTypeSequence)
|
||||||
dest.writeInt(if (enableAutoType == null) -1 else if (enableAutoType!!) 1 else 0)
|
dest.writeInt(if (enableAutoType == null) -1 else if (enableAutoType!!) 1 else 0)
|
||||||
dest.writeInt(if (enableSearching == null) -1 else if (enableSearching!!) 1 else 0)
|
dest.writeInt(if (enableSearching == null) -1 else if (enableSearching!!) 1 else 0)
|
||||||
dest.writeSerializable(lastTopVisibleEntry)
|
dest.writeSerializable(lastTopVisibleEntry)
|
||||||
|
dest.writeParcelable(tags, flags)
|
||||||
|
dest.writeParcelable(ParcelUuid(previousParentGroup), flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateWith(source: GroupKDBX) {
|
fun updateWith(source: GroupKDBX) {
|
||||||
@@ -97,34 +107,21 @@ class GroupKDBX : GroupVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
|||||||
usageCount = source.usageCount
|
usageCount = source.usageCount
|
||||||
locationChanged = DateInstant(source.locationChanged)
|
locationChanged = DateInstant(source.locationChanged)
|
||||||
// Add all custom elements in map
|
// Add all custom elements in map
|
||||||
customData.clear()
|
customData = CustomData(source.customData)
|
||||||
for ((key, value) in source.customData) {
|
|
||||||
customData[key] = value
|
|
||||||
}
|
|
||||||
notes = source.notes
|
notes = source.notes
|
||||||
isExpanded = source.isExpanded
|
isExpanded = source.isExpanded
|
||||||
defaultAutoTypeSequence = source.defaultAutoTypeSequence
|
defaultAutoTypeSequence = source.defaultAutoTypeSequence
|
||||||
enableAutoType = source.enableAutoType
|
enableAutoType = source.enableAutoType
|
||||||
enableSearching = source.enableSearching
|
enableSearching = source.enableSearching
|
||||||
lastTopVisibleEntry = source.lastTopVisibleEntry
|
lastTopVisibleEntry = source.lastTopVisibleEntry
|
||||||
|
tags = source.tags
|
||||||
|
previousParentGroup = source.previousParentGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
override var usageCount = UnsignedLong(0)
|
|
||||||
|
|
||||||
override var locationChanged = DateInstant()
|
|
||||||
|
|
||||||
override fun afterAssignNewParent() {
|
override fun afterAssignNewParent() {
|
||||||
locationChanged = DateInstant()
|
locationChanged = DateInstant()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putCustomData(key: String, value: String) {
|
|
||||||
customData[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun containsCustomData(): Boolean {
|
|
||||||
return customData.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
|
|||||||
@@ -22,27 +22,38 @@ package com.kunzisoft.keepass.database.element.icon
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.ParcelUuid
|
import android.os.ParcelUuid
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class IconImageCustom : IconImageDraw {
|
class IconImageCustom : IconImageDraw {
|
||||||
|
|
||||||
var uuid: UUID
|
val uuid: UUID
|
||||||
|
var name: String = ""
|
||||||
|
var lastModificationTime: DateInstant? = null
|
||||||
|
|
||||||
constructor() {
|
constructor(name: String = "", lastModificationTime: DateInstant? = null) {
|
||||||
uuid = DatabaseVersioned.UUID_ZERO
|
this.uuid = DatabaseVersioned.UUID_ZERO
|
||||||
|
this.name = name
|
||||||
|
this.lastModificationTime = lastModificationTime
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(uuid: UUID) {
|
constructor(uuid: UUID, name: String = "", lastModificationTime: DateInstant? = null) {
|
||||||
this.uuid = uuid
|
this.uuid = uuid
|
||||||
|
this.name = name
|
||||||
|
this.lastModificationTime = lastModificationTime
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(parcel: Parcel) {
|
constructor(parcel: Parcel) {
|
||||||
uuid = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: DatabaseVersioned.UUID_ZERO
|
uuid = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: DatabaseVersioned.UUID_ZERO
|
||||||
|
name = parcel.readString() ?: name
|
||||||
|
lastModificationTime = parcel.readParcelable(DateInstant::class.java.classLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
dest.writeParcelable(ParcelUuid(uuid), flags)
|
dest.writeParcelable(ParcelUuid(uuid), flags)
|
||||||
|
dest.writeString(name)
|
||||||
|
dest.writeParcelable(lastModificationTime, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun describeContents(): Int {
|
override fun describeContents(): Int {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package com.kunzisoft.keepass.database.element.icon
|
package com.kunzisoft.keepass.database.element.icon
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.binary.BinaryCache
|
import com.kunzisoft.keepass.database.element.binary.BinaryCache
|
||||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||||
import com.kunzisoft.keepass.database.element.binary.CustomIconPool
|
import com.kunzisoft.keepass.database.element.binary.CustomIconPool
|
||||||
@@ -27,7 +28,7 @@ import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.K
|
|||||||
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
|
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class IconsManager(private val binaryCache: BinaryCache) {
|
class IconsManager(binaryCache: BinaryCache) {
|
||||||
|
|
||||||
private val standardCache = List(NB_ICONS) {
|
private val standardCache = List(NB_ICONS) {
|
||||||
IconImageStandard(it)
|
IconImageStandard(it)
|
||||||
@@ -52,17 +53,15 @@ class IconsManager(private val binaryCache: BinaryCache) {
|
|||||||
fun buildNewCustomIcon(key: UUID? = null,
|
fun buildNewCustomIcon(key: UUID? = null,
|
||||||
result: (IconImageCustom, BinaryData?) -> Unit) {
|
result: (IconImageCustom, BinaryData?) -> Unit) {
|
||||||
// Create a binary file for a brand new custom icon
|
// Create a binary file for a brand new custom icon
|
||||||
addCustomIcon(key, false, result)
|
addCustomIcon(key, "", null, false, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCustomIcon(key: UUID? = null,
|
fun addCustomIcon(key: UUID? = null,
|
||||||
|
name: String,
|
||||||
|
lastModificationTime: DateInstant?,
|
||||||
smallSize: Boolean,
|
smallSize: Boolean,
|
||||||
result: (IconImageCustom, BinaryData?) -> Unit) {
|
result: (IconImageCustom, BinaryData?) -> Unit) {
|
||||||
val keyBinary = customCache.put(key) { uniqueBinaryId ->
|
customCache.put(key, name, lastModificationTime, smallSize, result)
|
||||||
// Create a byte array for better performance with small data
|
|
||||||
binaryCache.getBinaryData(uniqueBinaryId, smallSize)
|
|
||||||
}
|
|
||||||
result.invoke(IconImageCustom(keyBinary.keys.first()), keyBinary.binary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIcon(iconUuid: UUID): IconImageCustom {
|
fun getIcon(iconUuid: UUID): IconImageCustom {
|
||||||
@@ -88,8 +87,12 @@ class IconsManager(private val binaryCache: BinaryCache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun doForEachCustomIcon(action: (IconImageCustom, BinaryData) -> Unit) {
|
fun doForEachCustomIcon(action: (IconImageCustom, BinaryData) -> Unit) {
|
||||||
customCache.doForEachBinary { key, binary ->
|
customCache.doForEachCustomIcon(action)
|
||||||
action.invoke(IconImageCustom(key), binary)
|
}
|
||||||
|
|
||||||
|
fun containsCustomIconWithNameOrLastModificationTime(): Boolean {
|
||||||
|
return customCache.any { customIcon ->
|
||||||
|
customIcon.name.isNotEmpty() || customIcon.lastModificationTime != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,17 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.element.node
|
package com.kunzisoft.keepass.database.element.node
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.element.CustomData
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
|
import com.kunzisoft.keepass.database.element.Tags
|
||||||
import com.kunzisoft.keepass.utils.UnsignedLong
|
import com.kunzisoft.keepass.utils.UnsignedLong
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
interface NodeKDBXInterface : NodeTimeInterface {
|
interface NodeKDBXInterface : NodeTimeInterface {
|
||||||
|
|
||||||
var usageCount: UnsignedLong
|
var usageCount: UnsignedLong
|
||||||
|
|
||||||
var locationChanged: DateInstant
|
var locationChanged: DateInstant
|
||||||
|
var customData: CustomData
|
||||||
fun putCustomData(key: String, value: String)
|
var tags: Tags
|
||||||
|
var previousParentGroup: UUID
|
||||||
fun containsCustomData(): Boolean
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,7 @@ import com.kunzisoft.keepass.database.element.icon.IconImage
|
|||||||
interface NodeVersionedInterface<ParentGroup> : NodeTimeInterface, Parcelable {
|
interface NodeVersionedInterface<ParentGroup> : NodeTimeInterface, Parcelable {
|
||||||
|
|
||||||
var title: String
|
var title: String
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Visual icon
|
|
||||||
*/
|
|
||||||
var icon: IconImage
|
var icon: IconImage
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Type of Node
|
|
||||||
*/
|
|
||||||
val type: Type
|
val type: Type
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.file
|
package com.kunzisoft.keepass.database.file
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
|
|
||||||
import com.kunzisoft.encrypt.HashManager
|
import com.kunzisoft.encrypt.HashManager
|
||||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||||
|
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.VariantDictionary
|
import com.kunzisoft.keepass.database.crypto.VariantDictionary
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.AesKdf
|
import com.kunzisoft.keepass.database.crypto.kdf.AesKdf
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
||||||
@@ -91,41 +91,64 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
|||||||
this.masterSeed = ByteArray(32)
|
this.masterSeed = ByteArray(32)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class NodeHasCustomData<T: NodeKDBXInterface> : NodeHandler<T>() {
|
private open class NodeOperationHandler<T: NodeKDBXInterface> : NodeHandler<T>() {
|
||||||
|
var containsCustomData = false
|
||||||
internal var containsCustomData = false
|
|
||||||
|
|
||||||
override fun operate(node: T): Boolean {
|
override fun operate(node: T): Boolean {
|
||||||
if (node.containsCustomData()) {
|
if (node.customData.isNotEmpty()) {
|
||||||
containsCustomData = true
|
containsCustomData = true
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMinKdbxVersion(databaseV4: DatabaseKDBX): UnsignedInt {
|
private inner class EntryOperationHandler: NodeOperationHandler<EntryKDBX>() {
|
||||||
|
var passwordQualityEstimationDisabled = false
|
||||||
|
override fun operate(node: EntryKDBX): Boolean {
|
||||||
|
if (!node.qualityCheck) {
|
||||||
|
passwordQualityEstimationDisabled = true
|
||||||
|
}
|
||||||
|
return super.operate(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class GroupOperationHandler: NodeOperationHandler<GroupKDBX>() {
|
||||||
|
var containsTags = false
|
||||||
|
override fun operate(node: GroupKDBX): Boolean {
|
||||||
|
if (!node.tags.isEmpty())
|
||||||
|
containsTags = true
|
||||||
|
return super.operate(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMinKdbxVersion(databaseKDBX: DatabaseKDBX): UnsignedInt {
|
||||||
|
val entryHandler = EntryOperationHandler()
|
||||||
|
val groupHandler = GroupOperationHandler()
|
||||||
|
databaseKDBX.rootGroup?.doForEachChildAndForIt(entryHandler, groupHandler)
|
||||||
|
|
||||||
|
// https://keepass.info/help/kb/kdbx_4.1.html
|
||||||
|
val containsGroupWithTag = groupHandler.containsTags
|
||||||
|
val containsEntryWithPasswordQualityEstimationDisabled = entryHandler.passwordQualityEstimationDisabled
|
||||||
|
val containsCustomIconWithNameOrLastModificationTime = databaseKDBX.iconsManager.containsCustomIconWithNameOrLastModificationTime()
|
||||||
|
val containsHeaderCustomDataWithLastModificationTime = databaseKDBX.customData.containsItemWithLastModificationTime()
|
||||||
|
|
||||||
// https://keepass.info/help/kb/kdbx_4.html
|
// https://keepass.info/help/kb/kdbx_4.html
|
||||||
|
// If AES is not use, it's at least 4.0
|
||||||
|
val kdfIsNotAes = databaseKDBX.kdfParameters?.uuid != AesKdf.CIPHER_UUID
|
||||||
|
val containsHeaderCustomData = databaseKDBX.customData.isNotEmpty()
|
||||||
|
val containsNodeCustomData = entryHandler.containsCustomData || groupHandler.containsCustomData
|
||||||
|
|
||||||
// Return v4 if AES is not use
|
// Check each condition to determine version
|
||||||
if (databaseV4.kdfParameters != null
|
return if (containsGroupWithTag
|
||||||
&& databaseV4.kdfParameters!!.uuid != AesKdf.CIPHER_UUID) {
|
|| containsEntryWithPasswordQualityEstimationDisabled
|
||||||
return FILE_VERSION_32_4
|
|| containsCustomIconWithNameOrLastModificationTime
|
||||||
}
|
|| containsHeaderCustomDataWithLastModificationTime) {
|
||||||
|
FILE_VERSION_41
|
||||||
if (databaseV4.rootGroup == null) {
|
} else if (kdfIsNotAes
|
||||||
return FILE_VERSION_32_3
|
|| containsHeaderCustomData
|
||||||
}
|
|| containsNodeCustomData) {
|
||||||
|
FILE_VERSION_40
|
||||||
val entryHandler = NodeHasCustomData<EntryKDBX>()
|
|
||||||
val groupHandler = NodeHasCustomData<GroupKDBX>()
|
|
||||||
databaseV4.rootGroup?.doForEachChildAndForIt(entryHandler, groupHandler)
|
|
||||||
return if (databaseV4.containsCustomData()
|
|
||||||
|| entryHandler.containsCustomData
|
|
||||||
|| groupHandler.containsCustomData) {
|
|
||||||
FILE_VERSION_32_4
|
|
||||||
} else {
|
} else {
|
||||||
FILE_VERSION_32_3
|
FILE_VERSION_31
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +190,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
|||||||
private fun readHeaderField(dis: InputStream): Boolean {
|
private fun readHeaderField(dis: InputStream): Boolean {
|
||||||
val fieldID = dis.read().toByte()
|
val fieldID = dis.read().toByte()
|
||||||
|
|
||||||
val fieldSize: Int = if (version.isBefore(FILE_VERSION_32_4)) {
|
val fieldSize: Int = if (version.isBefore(FILE_VERSION_40)) {
|
||||||
dis.readBytes2ToUShort()
|
dis.readBytes2ToUShort()
|
||||||
} else {
|
} else {
|
||||||
dis.readBytes4ToUInt().toKotlinInt()
|
dis.readBytes4ToUInt().toKotlinInt()
|
||||||
@@ -194,20 +217,20 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
|||||||
|
|
||||||
PwDbHeaderV4Fields.MasterSeed -> masterSeed = fieldData
|
PwDbHeaderV4Fields.MasterSeed -> masterSeed = fieldData
|
||||||
|
|
||||||
PwDbHeaderV4Fields.TransformSeed -> if (version.isBefore(FILE_VERSION_32_4))
|
PwDbHeaderV4Fields.TransformSeed -> if (version.isBefore(FILE_VERSION_40))
|
||||||
transformSeed = fieldData
|
transformSeed = fieldData
|
||||||
|
|
||||||
PwDbHeaderV4Fields.TransformRounds -> if (version.isBefore(FILE_VERSION_32_4))
|
PwDbHeaderV4Fields.TransformRounds -> if (version.isBefore(FILE_VERSION_40))
|
||||||
setTransformRound(fieldData)
|
setTransformRound(fieldData)
|
||||||
|
|
||||||
PwDbHeaderV4Fields.EncryptionIV -> encryptionIV = fieldData
|
PwDbHeaderV4Fields.EncryptionIV -> encryptionIV = fieldData
|
||||||
|
|
||||||
PwDbHeaderV4Fields.InnerRandomstreamKey -> if (version.isBefore(FILE_VERSION_32_4))
|
PwDbHeaderV4Fields.InnerRandomstreamKey -> if (version.isBefore(FILE_VERSION_40))
|
||||||
innerRandomStreamKey = fieldData
|
innerRandomStreamKey = fieldData
|
||||||
|
|
||||||
PwDbHeaderV4Fields.StreamStartBytes -> streamStartBytes = fieldData
|
PwDbHeaderV4Fields.StreamStartBytes -> streamStartBytes = fieldData
|
||||||
|
|
||||||
PwDbHeaderV4Fields.InnerRandomStreamID -> if (version.isBefore(FILE_VERSION_32_4))
|
PwDbHeaderV4Fields.InnerRandomStreamID -> if (version.isBefore(FILE_VERSION_40))
|
||||||
setRandomStreamID(fieldData)
|
setRandomStreamID(fieldData)
|
||||||
|
|
||||||
PwDbHeaderV4Fields.KdfParameters -> databaseV4.kdfParameters = KdfParameters.deserialize(fieldData)
|
PwDbHeaderV4Fields.KdfParameters -> databaseV4.kdfParameters = KdfParameters.deserialize(fieldData)
|
||||||
@@ -283,7 +306,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
|||||||
*/
|
*/
|
||||||
private fun validVersion(version: UnsignedInt): Boolean {
|
private fun validVersion(version: UnsignedInt): Boolean {
|
||||||
return version.toKotlinInt() and FILE_VERSION_CRITICAL_MASK.toKotlinInt() <=
|
return version.toKotlinInt() and FILE_VERSION_CRITICAL_MASK.toKotlinInt() <=
|
||||||
FILE_VERSION_32_4.toKotlinInt() and FILE_VERSION_CRITICAL_MASK.toKotlinInt()
|
FILE_VERSION_40.toKotlinInt() and FILE_VERSION_CRITICAL_MASK.toKotlinInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -292,8 +315,9 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
|||||||
val DBSIG_2 = UnsignedInt(-0x4ab40499)
|
val DBSIG_2 = UnsignedInt(-0x4ab40499)
|
||||||
|
|
||||||
private val FILE_VERSION_CRITICAL_MASK = UnsignedInt(-0x10000)
|
private val FILE_VERSION_CRITICAL_MASK = UnsignedInt(-0x10000)
|
||||||
val FILE_VERSION_32_3 = UnsignedInt(0x00030001)
|
val FILE_VERSION_31 = UnsignedInt(0x00030001)
|
||||||
val FILE_VERSION_32_4 = UnsignedInt(0x00040000)
|
val FILE_VERSION_40 = UnsignedInt(0x00040000)
|
||||||
|
val FILE_VERSION_41 = UnsignedInt(0x00040001)
|
||||||
|
|
||||||
fun getCompressionFromFlag(flag: UnsignedInt): CompressionAlgorithm? {
|
fun getCompressionFromFlag(flag: UnsignedInt): CompressionAlgorithm? {
|
||||||
return when (flag.toKotlinInt()) {
|
return when (flag.toKotlinInt()) {
|
||||||
|
|||||||
@@ -79,8 +79,10 @@ object DatabaseKDBXXML {
|
|||||||
const val ElemFgColor = "ForegroundColor"
|
const val ElemFgColor = "ForegroundColor"
|
||||||
const val ElemBgColor = "BackgroundColor"
|
const val ElemBgColor = "BackgroundColor"
|
||||||
const val ElemOverrideUrl = "OverrideURL"
|
const val ElemOverrideUrl = "OverrideURL"
|
||||||
|
const val ElemQualityCheck = "QualityCheck"
|
||||||
const val ElemTimes = "Times"
|
const val ElemTimes = "Times"
|
||||||
const val ElemTags = "Tags"
|
const val ElemTags = "Tags"
|
||||||
|
const val ElemPreviousParentGroup = "PreviousParentGroup"
|
||||||
|
|
||||||
const val ElemCreationTime = "CreationTime"
|
const val ElemCreationTime = "CreationTime"
|
||||||
const val ElemLastModTime = "LastModificationTime"
|
const val ElemLastModTime = "LastModificationTime"
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ import com.kunzisoft.keepass.database.crypto.CipherEngine
|
|||||||
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
|
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.HmacBlock
|
import com.kunzisoft.keepass.database.crypto.HmacBlock
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.*
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
|
||||||
import com.kunzisoft.keepass.database.element.DeletedObject
|
|
||||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||||
import com.kunzisoft.keepass.database.element.binary.LoadedKey
|
import com.kunzisoft.keepass.database.element.binary.LoadedKey
|
||||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||||
@@ -42,7 +40,7 @@ import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface
|
|||||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
import com.kunzisoft.keepass.database.exception.*
|
import com.kunzisoft.keepass.database.exception.*
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
|
import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
|
||||||
import com.kunzisoft.keepass.database.file.DateKDBXUtil
|
import com.kunzisoft.keepass.database.file.DateKDBXUtil
|
||||||
import com.kunzisoft.keepass.stream.HashedBlockInputStream
|
import com.kunzisoft.keepass.stream.HashedBlockInputStream
|
||||||
@@ -88,9 +86,12 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
private var ctxHistoryBase: EntryKDBX? = null
|
private var ctxHistoryBase: EntryKDBX? = null
|
||||||
private var ctxDeletedObject: DeletedObject? = null
|
private var ctxDeletedObject: DeletedObject? = null
|
||||||
private var customIconID = DatabaseVersioned.UUID_ZERO
|
private var customIconID = DatabaseVersioned.UUID_ZERO
|
||||||
|
private var customIconName: String = ""
|
||||||
|
private var customIconLastModificationTime: DateInstant? = null
|
||||||
private var customIconData: ByteArray? = null
|
private var customIconData: ByteArray? = null
|
||||||
private var customDataKey: String? = null
|
private var customDataKey: String? = null
|
||||||
private var customDataValue: String? = null
|
private var customDataValue: String? = null
|
||||||
|
private var customDataLastModificationTime: DateInstant? = null
|
||||||
private var groupCustomDataKey: String? = null
|
private var groupCustomDataKey: String? = null
|
||||||
private var groupCustomDataValue: String? = null
|
private var groupCustomDataValue: String? = null
|
||||||
private var entryCustomDataKey: String? = null
|
private var entryCustomDataKey: String? = null
|
||||||
@@ -161,7 +162,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val plainInputStream: InputStream
|
val plainInputStream: InputStream
|
||||||
if (mDatabase.kdbxVersion.isBefore(FILE_VERSION_32_4)) {
|
if (mDatabase.kdbxVersion.isBefore(FILE_VERSION_40)) {
|
||||||
|
|
||||||
val dataDecrypted = CipherInputStream(databaseInputStream, cipher)
|
val dataDecrypted = CipherInputStream(databaseInputStream, cipher)
|
||||||
val storedStartBytes: ByteArray?
|
val storedStartBytes: ByteArray?
|
||||||
@@ -210,7 +211,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
else -> plainInputStream
|
else -> plainInputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mDatabase.kdbxVersion.isBefore(FILE_VERSION_32_4)) {
|
if (!mDatabase.kdbxVersion.isBefore(FILE_VERSION_40)) {
|
||||||
readInnerHeader(inputStreamXml, header)
|
readInnerHeader(inputStreamXml, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,25 +387,25 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemSettingsChanged, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemSettingsChanged, ignoreCase = true)) {
|
||||||
mDatabase.settingsChanged = readPwTime(xpp)
|
mDatabase.settingsChanged = readDateInstant(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbName, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbName, ignoreCase = true)) {
|
||||||
mDatabase.name = readString(xpp)
|
mDatabase.name = readString(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbNameChanged, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbNameChanged, ignoreCase = true)) {
|
||||||
mDatabase.nameChanged = readPwTime(xpp)
|
mDatabase.nameChanged = readDateInstant(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbDesc, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbDesc, ignoreCase = true)) {
|
||||||
mDatabase.description = readString(xpp)
|
mDatabase.description = readString(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbDescChanged, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbDescChanged, ignoreCase = true)) {
|
||||||
mDatabase.descriptionChanged = readPwTime(xpp)
|
mDatabase.descriptionChanged = readDateInstant(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbDefaultUser, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbDefaultUser, ignoreCase = true)) {
|
||||||
mDatabase.defaultUserName = readString(xpp)
|
mDatabase.defaultUserName = readString(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbDefaultUserChanged, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbDefaultUserChanged, ignoreCase = true)) {
|
||||||
mDatabase.defaultUserNameChanged = readPwTime(xpp)
|
mDatabase.defaultUserNameChanged = readDateInstant(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbColor, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbColor, ignoreCase = true)) {
|
||||||
mDatabase.color = readString(xpp)
|
mDatabase.color = readString(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbMntncHistoryDays, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbMntncHistoryDays, ignoreCase = true)) {
|
||||||
mDatabase.maintenanceHistoryDays = readUInt(xpp, DEFAULT_HISTORY_DAYS)
|
mDatabase.maintenanceHistoryDays = readUInt(xpp, DEFAULT_HISTORY_DAYS)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbKeyChanged, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbKeyChanged, ignoreCase = true)) {
|
||||||
mDatabase.keyLastChanged = readPwTime(xpp)
|
mDatabase.keyLastChanged = readDateInstant(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbKeyChangeRec, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbKeyChangeRec, ignoreCase = true)) {
|
||||||
mDatabase.keyChangeRecDays = readLong(xpp, -1)
|
mDatabase.keyChangeRecDays = readLong(xpp, -1)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDbKeyChangeForce, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDbKeyChangeForce, ignoreCase = true)) {
|
||||||
@@ -420,11 +421,11 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
} else if (name.equals(DatabaseKDBXXML.ElemRecycleBinUuid, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemRecycleBinUuid, ignoreCase = true)) {
|
||||||
mDatabase.recycleBinUUID = readUuid(xpp)
|
mDatabase.recycleBinUUID = readUuid(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemRecycleBinChanged, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemRecycleBinChanged, ignoreCase = true)) {
|
||||||
mDatabase.recycleBinChanged = readTime(xpp)
|
mDatabase.recycleBinChanged = readDateInstant(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemEntryTemplatesGroup, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemEntryTemplatesGroup, ignoreCase = true)) {
|
||||||
mDatabase.entryTemplatesGroup = readUuid(xpp)
|
mDatabase.entryTemplatesGroup = readUuid(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemEntryTemplatesGroupChanged, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemEntryTemplatesGroupChanged, ignoreCase = true)) {
|
||||||
mDatabase.entryTemplatesGroupChanged = readPwTime(xpp)
|
mDatabase.entryTemplatesGroupChanged = readDateInstant(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemHistoryMaxItems, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemHistoryMaxItems, ignoreCase = true)) {
|
||||||
mDatabase.historyMaxItems = readInt(xpp, -1)
|
mDatabase.historyMaxItems = readInt(xpp, -1)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemHistoryMaxSize, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemHistoryMaxSize, ignoreCase = true)) {
|
||||||
@@ -468,6 +469,10 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
if (strData.isNotEmpty()) {
|
if (strData.isNotEmpty()) {
|
||||||
customIconData = Base64.decode(strData, BASE_64_FLAG)
|
customIconData = Base64.decode(strData, BASE_64_FLAG)
|
||||||
}
|
}
|
||||||
|
} else if (name.equals(DatabaseKDBXXML.ElemName, ignoreCase = true)) {
|
||||||
|
customIconName = readString(xpp)
|
||||||
|
} else if (name.equals(DatabaseKDBXXML.ElemLastModTime, ignoreCase = true)) {
|
||||||
|
customIconLastModificationTime = readDateInstant(xpp)
|
||||||
} else {
|
} else {
|
||||||
readUnknown(xpp)
|
readUnknown(xpp)
|
||||||
}
|
}
|
||||||
@@ -488,6 +493,8 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
customDataKey = readString(xpp)
|
customDataKey = readString(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemValue, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemValue, ignoreCase = true)) {
|
||||||
customDataValue = readString(xpp)
|
customDataValue = readString(xpp)
|
||||||
|
} else if (name.equals(DatabaseKDBXXML.ElemLastModTime, ignoreCase = true)) {
|
||||||
|
customDataLastModificationTime = readDateInstant(xpp)
|
||||||
} else {
|
} else {
|
||||||
readUnknown(xpp)
|
readUnknown(xpp)
|
||||||
}
|
}
|
||||||
@@ -518,6 +525,10 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
ctxGroup?.icon?.standard = mDatabase.getStandardIcon(readUInt(xpp, UnsignedInt(0)).toKotlinInt())
|
ctxGroup?.icon?.standard = mDatabase.getStandardIcon(readUInt(xpp, UnsignedInt(0)).toKotlinInt())
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemCustomIconID, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemCustomIconID, ignoreCase = true)) {
|
||||||
ctxGroup?.icon?.custom = mDatabase.getCustomIcon(readUuid(xpp))
|
ctxGroup?.icon?.custom = mDatabase.getCustomIcon(readUuid(xpp))
|
||||||
|
} else if (name.equals(DatabaseKDBXXML.ElemTags, ignoreCase = true)) {
|
||||||
|
ctxGroup?.tags = readTags(xpp)
|
||||||
|
} else if (name.equals(DatabaseKDBXXML.ElemPreviousParentGroup, ignoreCase = true)) {
|
||||||
|
ctxGroup?.previousParentGroup = readUuid(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemTimes, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemTimes, ignoreCase = true)) {
|
||||||
return switchContext(ctx, KdbContext.GroupTimes, xpp)
|
return switchContext(ctx, KdbContext.GroupTimes, xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemIsExpanded, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemIsExpanded, ignoreCase = true)) {
|
||||||
@@ -562,6 +573,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
KdbContext.GroupCustomDataItem -> when {
|
KdbContext.GroupCustomDataItem -> when {
|
||||||
name.equals(DatabaseKDBXXML.ElemKey, ignoreCase = true) -> groupCustomDataKey = readString(xpp)
|
name.equals(DatabaseKDBXXML.ElemKey, ignoreCase = true) -> groupCustomDataKey = readString(xpp)
|
||||||
name.equals(DatabaseKDBXXML.ElemValue, ignoreCase = true) -> groupCustomDataValue = readString(xpp)
|
name.equals(DatabaseKDBXXML.ElemValue, ignoreCase = true) -> groupCustomDataValue = readString(xpp)
|
||||||
|
name.equals(DatabaseKDBXXML.ElemLastModTime, ignoreCase = true) -> readDateInstant(xpp) // Ignore
|
||||||
else -> readUnknown(xpp)
|
else -> readUnknown(xpp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,8 +590,12 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
ctxEntry?.backgroundColor = readString(xpp)
|
ctxEntry?.backgroundColor = readString(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemOverrideUrl, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemOverrideUrl, ignoreCase = true)) {
|
||||||
ctxEntry?.overrideURL = readString(xpp)
|
ctxEntry?.overrideURL = readString(xpp)
|
||||||
|
} else if (name.equals(DatabaseKDBXXML.ElemQualityCheck, ignoreCase = true)) {
|
||||||
|
ctxEntry?.qualityCheck = readBool(xpp, true)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemTags, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemTags, ignoreCase = true)) {
|
||||||
ctxEntry?.tags = readString(xpp)
|
ctxEntry?.tags = readTags(xpp)
|
||||||
|
} else if (name.equals(DatabaseKDBXXML.ElemPreviousParentGroup, ignoreCase = true)) {
|
||||||
|
ctxEntry?.previousParentGroup = readUuid(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemTimes, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemTimes, ignoreCase = true)) {
|
||||||
return switchContext(ctx, KdbContext.EntryTimes, xpp)
|
return switchContext(ctx, KdbContext.EntryTimes, xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemString, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemString, ignoreCase = true)) {
|
||||||
@@ -608,6 +624,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
KdbContext.EntryCustomDataItem -> when {
|
KdbContext.EntryCustomDataItem -> when {
|
||||||
name.equals(DatabaseKDBXXML.ElemKey, ignoreCase = true) -> entryCustomDataKey = readString(xpp)
|
name.equals(DatabaseKDBXXML.ElemKey, ignoreCase = true) -> entryCustomDataKey = readString(xpp)
|
||||||
name.equals(DatabaseKDBXXML.ElemValue, ignoreCase = true) -> entryCustomDataValue = readString(xpp)
|
name.equals(DatabaseKDBXXML.ElemValue, ignoreCase = true) -> entryCustomDataValue = readString(xpp)
|
||||||
|
name.equals(DatabaseKDBXXML.ElemLastModTime, ignoreCase = true) -> readDateInstant(xpp) // Ignore
|
||||||
else -> readUnknown(xpp)
|
else -> readUnknown(xpp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,13 +637,13 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
}
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
name.equals(DatabaseKDBXXML.ElemLastModTime, ignoreCase = true) -> tl?.lastModificationTime = readPwTime(xpp)
|
name.equals(DatabaseKDBXXML.ElemLastModTime, ignoreCase = true) -> tl?.lastModificationTime = readDateInstant(xpp)
|
||||||
name.equals(DatabaseKDBXXML.ElemCreationTime, ignoreCase = true) -> tl?.creationTime = readPwTime(xpp)
|
name.equals(DatabaseKDBXXML.ElemCreationTime, ignoreCase = true) -> tl?.creationTime = readDateInstant(xpp)
|
||||||
name.equals(DatabaseKDBXXML.ElemLastAccessTime, ignoreCase = true) -> tl?.lastAccessTime = readPwTime(xpp)
|
name.equals(DatabaseKDBXXML.ElemLastAccessTime, ignoreCase = true) -> tl?.lastAccessTime = readDateInstant(xpp)
|
||||||
name.equals(DatabaseKDBXXML.ElemExpiryTime, ignoreCase = true) -> tl?.expiryTime = readPwTime(xpp)
|
name.equals(DatabaseKDBXXML.ElemExpiryTime, ignoreCase = true) -> tl?.expiryTime = readDateInstant(xpp)
|
||||||
name.equals(DatabaseKDBXXML.ElemExpires, ignoreCase = true) -> tl?.expires = readBool(xpp, false)
|
name.equals(DatabaseKDBXXML.ElemExpires, ignoreCase = true) -> tl?.expires = readBool(xpp, false)
|
||||||
name.equals(DatabaseKDBXXML.ElemUsageCount, ignoreCase = true) -> tl?.usageCount = readULong(xpp, UnsignedLong(0))
|
name.equals(DatabaseKDBXXML.ElemUsageCount, ignoreCase = true) -> tl?.usageCount = readULong(xpp, UnsignedLong(0))
|
||||||
name.equals(DatabaseKDBXXML.ElemLocationChanged, ignoreCase = true) -> tl?.locationChanged = readPwTime(xpp)
|
name.equals(DatabaseKDBXXML.ElemLocationChanged, ignoreCase = true) -> tl?.locationChanged = readDateInstant(xpp)
|
||||||
else -> readUnknown(xpp)
|
else -> readUnknown(xpp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -687,7 +704,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
KdbContext.DeletedObject -> if (name.equals(DatabaseKDBXXML.ElemUuid, ignoreCase = true)) {
|
KdbContext.DeletedObject -> if (name.equals(DatabaseKDBXXML.ElemUuid, ignoreCase = true)) {
|
||||||
ctxDeletedObject?.uuid = readUuid(xpp)
|
ctxDeletedObject?.uuid = readUuid(xpp)
|
||||||
} else if (name.equals(DatabaseKDBXXML.ElemDeletionTime, ignoreCase = true)) {
|
} else if (name.equals(DatabaseKDBXXML.ElemDeletionTime, ignoreCase = true)) {
|
||||||
ctxDeletedObject?.setDeletionTime(readTime(xpp))
|
ctxDeletedObject?.setDeletionTime(readDateInstant(xpp))
|
||||||
} else {
|
} else {
|
||||||
readUnknown(xpp)
|
readUnknown(xpp)
|
||||||
}
|
}
|
||||||
@@ -714,29 +731,34 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
} else if (ctx == KdbContext.CustomIcon && name.equals(DatabaseKDBXXML.ElemCustomIconItem, ignoreCase = true)) {
|
} else if (ctx == KdbContext.CustomIcon && name.equals(DatabaseKDBXXML.ElemCustomIconItem, ignoreCase = true)) {
|
||||||
val iconData = customIconData
|
val iconData = customIconData
|
||||||
if (customIconID != DatabaseVersioned.UUID_ZERO && iconData != null) {
|
if (customIconID != DatabaseVersioned.UUID_ZERO && iconData != null) {
|
||||||
mDatabase.addCustomIcon(customIconID, isRAMSufficient.invoke(iconData.size.toLong())) { _, binary ->
|
mDatabase.addCustomIcon(customIconID,
|
||||||
|
customIconName,
|
||||||
|
customIconLastModificationTime,
|
||||||
|
isRAMSufficient.invoke(iconData.size.toLong())) { _, binary ->
|
||||||
binary?.getOutputDataStream(mDatabase.binaryCache)?.use { outputStream ->
|
binary?.getOutputDataStream(mDatabase.binaryCache)?.use { outputStream ->
|
||||||
outputStream.write(iconData)
|
outputStream.write(iconData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customIconID = DatabaseVersioned.UUID_ZERO
|
customIconID = DatabaseVersioned.UUID_ZERO
|
||||||
|
customIconName = ""
|
||||||
|
customIconLastModificationTime = null
|
||||||
customIconData = null
|
customIconData = null
|
||||||
|
|
||||||
return KdbContext.CustomIcons
|
return KdbContext.CustomIcons
|
||||||
} else if (ctx == KdbContext.Binaries && name.equals(DatabaseKDBXXML.ElemBinaries, ignoreCase = true)) {
|
} else if (ctx == KdbContext.Binaries && name.equals(DatabaseKDBXXML.ElemBinaries, ignoreCase = true)) {
|
||||||
return KdbContext.Meta
|
return KdbContext.Meta
|
||||||
} else if (ctx == KdbContext.CustomData && name.equals(DatabaseKDBXXML.ElemCustomData, ignoreCase = true)) {
|
} else if (ctx == KdbContext.CustomData && name.equals(DatabaseKDBXXML.ElemCustomData, ignoreCase = true)) {
|
||||||
return KdbContext.Meta
|
return KdbContext.Meta
|
||||||
} else if (ctx == KdbContext.CustomDataItem && name.equals(DatabaseKDBXXML.ElemStringDictExItem, ignoreCase = true)) {
|
} else if (ctx == KdbContext.CustomDataItem && name.equals(DatabaseKDBXXML.ElemStringDictExItem, ignoreCase = true)) {
|
||||||
if (customDataKey != null && customDataValue != null) {
|
customDataKey?.let { dataKey ->
|
||||||
mDatabase.putCustomData(customDataKey!!, customDataValue!!)
|
customDataValue?.let { dataValue ->
|
||||||
|
mDatabase.customData.put(CustomDataItem(dataKey,
|
||||||
|
dataValue, customDataLastModificationTime))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customDataKey = null
|
customDataKey = null
|
||||||
customDataValue = null
|
customDataValue = null
|
||||||
|
customDataLastModificationTime = null
|
||||||
return KdbContext.CustomData
|
return KdbContext.CustomData
|
||||||
} else if (ctx == KdbContext.Group && name.equals(DatabaseKDBXXML.ElemGroup, ignoreCase = true)) {
|
} else if (ctx == KdbContext.Group && name.equals(DatabaseKDBXXML.ElemGroup, ignoreCase = true)) {
|
||||||
if (ctxGroup != null && ctxGroup?.id == DatabaseVersioned.UUID_ZERO) {
|
if (ctxGroup != null && ctxGroup?.id == DatabaseVersioned.UUID_ZERO) {
|
||||||
@@ -758,13 +780,13 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
} else if (ctx == KdbContext.GroupCustomData && name.equals(DatabaseKDBXXML.ElemCustomData, ignoreCase = true)) {
|
} else if (ctx == KdbContext.GroupCustomData && name.equals(DatabaseKDBXXML.ElemCustomData, ignoreCase = true)) {
|
||||||
return KdbContext.Group
|
return KdbContext.Group
|
||||||
} else if (ctx == KdbContext.GroupCustomDataItem && name.equals(DatabaseKDBXXML.ElemStringDictExItem, ignoreCase = true)) {
|
} else if (ctx == KdbContext.GroupCustomDataItem && name.equals(DatabaseKDBXXML.ElemStringDictExItem, ignoreCase = true)) {
|
||||||
if (groupCustomDataKey != null && groupCustomDataValue != null) {
|
groupCustomDataKey?.let { customDataKey ->
|
||||||
ctxGroup?.putCustomData(groupCustomDataKey!!, groupCustomDataValue!!)
|
groupCustomDataValue?.let { customDataValue ->
|
||||||
|
ctxGroup?.customData?.put(CustomDataItem(customDataKey, customDataValue))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
groupCustomDataKey = null
|
groupCustomDataKey = null
|
||||||
groupCustomDataValue = null
|
groupCustomDataValue = null
|
||||||
|
|
||||||
return KdbContext.GroupCustomData
|
return KdbContext.GroupCustomData
|
||||||
|
|
||||||
} else if (ctx == KdbContext.Entry && name.equals(DatabaseKDBXXML.ElemEntry, ignoreCase = true)) {
|
} else if (ctx == KdbContext.Entry && name.equals(DatabaseKDBXXML.ElemEntry, ignoreCase = true)) {
|
||||||
@@ -810,13 +832,13 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
} else if (ctx == KdbContext.EntryCustomData && name.equals(DatabaseKDBXXML.ElemCustomData, ignoreCase = true)) {
|
} else if (ctx == KdbContext.EntryCustomData && name.equals(DatabaseKDBXXML.ElemCustomData, ignoreCase = true)) {
|
||||||
return KdbContext.Entry
|
return KdbContext.Entry
|
||||||
} else if (ctx == KdbContext.EntryCustomDataItem && name.equals(DatabaseKDBXXML.ElemStringDictExItem, ignoreCase = true)) {
|
} else if (ctx == KdbContext.EntryCustomDataItem && name.equals(DatabaseKDBXXML.ElemStringDictExItem, ignoreCase = true)) {
|
||||||
if (entryCustomDataKey != null && entryCustomDataValue != null) {
|
entryCustomDataKey?.let { customDataKey ->
|
||||||
ctxEntry?.putCustomData(entryCustomDataKey!!, entryCustomDataValue!!)
|
entryCustomDataValue?.let { customDataValue ->
|
||||||
|
ctxEntry?.customData?.put(CustomDataItem(customDataKey, customDataValue))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entryCustomDataKey = null
|
entryCustomDataKey = null
|
||||||
entryCustomDataValue = null
|
entryCustomDataValue = null
|
||||||
|
|
||||||
return KdbContext.EntryCustomData
|
return KdbContext.EntryCustomData
|
||||||
} else if (ctx == KdbContext.EntryHistory && name.equals(DatabaseKDBXXML.ElemHistory, ignoreCase = true)) {
|
} else if (ctx == KdbContext.EntryHistory && name.equals(DatabaseKDBXXML.ElemHistory, ignoreCase = true)) {
|
||||||
entryInHistory = false
|
entryInHistory = false
|
||||||
@@ -836,16 +858,11 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class, XmlPullParserException::class)
|
@Throws(IOException::class, XmlPullParserException::class)
|
||||||
private fun readPwTime(xpp: XmlPullParser): DateInstant {
|
private fun readDateInstant(xpp: XmlPullParser): DateInstant {
|
||||||
return DateInstant(readTime(xpp))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class, XmlPullParserException::class)
|
|
||||||
private fun readTime(xpp: XmlPullParser): Date {
|
|
||||||
val sDate = readString(xpp)
|
val sDate = readString(xpp)
|
||||||
var utcDate: Date? = null
|
var utcDate: Date? = null
|
||||||
|
|
||||||
if (mDatabase.kdbxVersion.isBefore(FILE_VERSION_32_4)) {
|
if (mDatabase.kdbxVersion.isBefore(FILE_VERSION_40)) {
|
||||||
try {
|
try {
|
||||||
utcDate = DatabaseKDBXXML.DateFormatter.parse(sDate)
|
utcDate = DatabaseKDBXXML.DateFormatter.parse(sDate)
|
||||||
} catch (e: ParseException) {
|
} catch (e: ParseException) {
|
||||||
@@ -863,7 +880,12 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
|||||||
utcDate = DateKDBXUtil.convertKDBX4Time(seconds)
|
utcDate = DateKDBXUtil.convertKDBX4Time(seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
return utcDate ?: Date(0L)
|
return DateInstant(utcDate ?: Date(0L))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, XmlPullParserException::class)
|
||||||
|
private fun readTags(xpp: XmlPullParser): Tags {
|
||||||
|
return Tags(readString(xpp))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class)
|
@Throws(XmlPullParserException::class, IOException::class)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
|||||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeader
|
import com.kunzisoft.keepass.database.file.DatabaseHeader
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
|
||||||
import com.kunzisoft.keepass.stream.MacOutputStream
|
import com.kunzisoft.keepass.stream.MacOutputStream
|
||||||
import com.kunzisoft.keepass.utils.*
|
import com.kunzisoft.keepass.utils.*
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
@@ -76,7 +76,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
|
|||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, uIntTo4Bytes(DatabaseHeaderKDBX.getFlagFromCompression(databaseKDBX.compressionAlgorithm)))
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.CompressionFlags, uIntTo4Bytes(DatabaseHeaderKDBX.getFlagFromCompression(databaseKDBX.compressionAlgorithm)))
|
||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed)
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.MasterSeed, header.masterSeed)
|
||||||
|
|
||||||
if (header.version.isBefore(FILE_VERSION_32_4)) {
|
if (header.version.isBefore(FILE_VERSION_40)) {
|
||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformSeed, header.transformSeed)
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformSeed, header.transformSeed)
|
||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, longTo8Bytes(databaseKDBX.numberKeyEncryptionRounds))
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.TransformRounds, longTo8Bytes(databaseKDBX.numberKeyEncryptionRounds))
|
||||||
} else {
|
} else {
|
||||||
@@ -87,7 +87,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
|
|||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV)
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.version.isBefore(FILE_VERSION_32_4)) {
|
if (header.version.isBefore(FILE_VERSION_40)) {
|
||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey)
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey)
|
||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes)
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes)
|
||||||
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomStreamID, uIntTo4Bytes(header.innerRandomStream!!.id))
|
writeHeaderField(DatabaseHeaderKDBX.PwDbHeaderV4Fields.InnerRandomStreamID, uIntTo4Bytes(header.innerRandomStream!!.id))
|
||||||
@@ -121,7 +121,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun writeHeaderFieldSize(size: Int) {
|
private fun writeHeaderFieldSize(size: Int) {
|
||||||
if (header.version.isBefore(FILE_VERSION_32_4)) {
|
if (header.version.isBefore(FILE_VERSION_40)) {
|
||||||
mos.write2BytesUShort(size)
|
mos.write2BytesUShort(size)
|
||||||
} else {
|
} else {
|
||||||
mos.write4BytesUInt(UnsignedInt(size))
|
mos.write4BytesUInt(UnsignedInt(size))
|
||||||
|
|||||||
@@ -23,15 +23,16 @@ import android.util.Base64
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Xml
|
import android.util.Xml
|
||||||
import com.kunzisoft.encrypt.StreamCipher
|
import com.kunzisoft.encrypt.StreamCipher
|
||||||
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
|
|
||||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||||
import com.kunzisoft.keepass.database.crypto.CipherEngine
|
import com.kunzisoft.keepass.database.crypto.CipherEngine
|
||||||
|
import com.kunzisoft.keepass.database.crypto.CrsAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
||||||
import com.kunzisoft.keepass.database.element.DeletedObject
|
import com.kunzisoft.keepass.database.element.*
|
||||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX.Companion.BASE_64_FLAG
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX.Companion.BASE_64_FLAG
|
||||||
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
import com.kunzisoft.keepass.database.element.entry.AutoType
|
import com.kunzisoft.keepass.database.element.entry.AutoType
|
||||||
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
||||||
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
||||||
@@ -41,7 +42,8 @@ import com.kunzisoft.keepass.database.element.security.ProtectedString
|
|||||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||||
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
|
||||||
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_41
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
|
import com.kunzisoft.keepass.database.file.DatabaseKDBXXML
|
||||||
import com.kunzisoft.keepass.database.file.DateKDBXUtil
|
import com.kunzisoft.keepass.database.file.DateKDBXUtil
|
||||||
import com.kunzisoft.keepass.stream.HashedBlockOutputStream
|
import com.kunzisoft.keepass.stream.HashedBlockOutputStream
|
||||||
@@ -83,7 +85,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
|
|
||||||
header = outputHeader(mOutputStream)
|
header = outputHeader(mOutputStream)
|
||||||
|
|
||||||
val osPlain: OutputStream = if (header!!.version.isBefore(FILE_VERSION_32_4)) {
|
val osPlain: OutputStream = if (header!!.version.isBefore(FILE_VERSION_40)) {
|
||||||
val cos = attachStreamEncryptor(header!!, mOutputStream)
|
val cos = attachStreamEncryptor(header!!, mOutputStream)
|
||||||
cos.write(header!!.streamStartBytes)
|
cos.write(header!!.streamStartBytes)
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
else -> osPlain
|
else -> osPlain
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!header!!.version.isBefore(FILE_VERSION_32_4)) {
|
if (!header!!.version.isBefore(FILE_VERSION_40)) {
|
||||||
outputInnerHeader(mDatabaseKDBX, header!!, xmlOutputStream)
|
outputInnerHeader(mDatabaseKDBX, header!!, xmlOutputStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,40 +236,40 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
private fun writeMeta() {
|
private fun writeMeta() {
|
||||||
xml.startTag(null, DatabaseKDBXXML.ElemMeta)
|
xml.startTag(null, DatabaseKDBXXML.ElemMeta)
|
||||||
|
|
||||||
writeObject(DatabaseKDBXXML.ElemGenerator, mDatabaseKDBX.localizedAppName)
|
writeString(DatabaseKDBXXML.ElemGenerator, mDatabaseKDBX.localizedAppName)
|
||||||
|
|
||||||
if (hashOfHeader != null) {
|
if (hashOfHeader != null) {
|
||||||
writeObject(DatabaseKDBXXML.ElemHeaderHash, String(Base64.encode(hashOfHeader!!, BASE_64_FLAG)))
|
writeString(DatabaseKDBXXML.ElemHeaderHash, String(Base64.encode(hashOfHeader!!, BASE_64_FLAG)))
|
||||||
}
|
}
|
||||||
|
|
||||||
writeObject(DatabaseKDBXXML.ElemDbName, mDatabaseKDBX.name, true)
|
writeString(DatabaseKDBXXML.ElemDbName, mDatabaseKDBX.name, true)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbNameChanged, mDatabaseKDBX.nameChanged.date)
|
writeDateInstant(DatabaseKDBXXML.ElemDbNameChanged, mDatabaseKDBX.nameChanged)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbDesc, mDatabaseKDBX.description, true)
|
writeString(DatabaseKDBXXML.ElemDbDesc, mDatabaseKDBX.description, true)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbDescChanged, mDatabaseKDBX.descriptionChanged.date)
|
writeDateInstant(DatabaseKDBXXML.ElemDbDescChanged, mDatabaseKDBX.descriptionChanged)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbDefaultUser, mDatabaseKDBX.defaultUserName, true)
|
writeString(DatabaseKDBXXML.ElemDbDefaultUser, mDatabaseKDBX.defaultUserName, true)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbDefaultUserChanged, mDatabaseKDBX.defaultUserNameChanged.date)
|
writeDateInstant(DatabaseKDBXXML.ElemDbDefaultUserChanged, mDatabaseKDBX.defaultUserNameChanged)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbMntncHistoryDays, mDatabaseKDBX.maintenanceHistoryDays.toKotlinLong())
|
writeLong(DatabaseKDBXXML.ElemDbMntncHistoryDays, mDatabaseKDBX.maintenanceHistoryDays.toKotlinLong())
|
||||||
writeObject(DatabaseKDBXXML.ElemDbColor, mDatabaseKDBX.color)
|
writeString(DatabaseKDBXXML.ElemDbColor, mDatabaseKDBX.color)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbKeyChanged, mDatabaseKDBX.keyLastChanged.date)
|
writeDateInstant(DatabaseKDBXXML.ElemDbKeyChanged, mDatabaseKDBX.keyLastChanged)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbKeyChangeRec, mDatabaseKDBX.keyChangeRecDays)
|
writeLong(DatabaseKDBXXML.ElemDbKeyChangeRec, mDatabaseKDBX.keyChangeRecDays)
|
||||||
writeObject(DatabaseKDBXXML.ElemDbKeyChangeForce, mDatabaseKDBX.keyChangeForceDays)
|
writeLong(DatabaseKDBXXML.ElemDbKeyChangeForce, mDatabaseKDBX.keyChangeForceDays)
|
||||||
|
|
||||||
writeMemoryProtection(mDatabaseKDBX.memoryProtection)
|
writeMemoryProtection(mDatabaseKDBX.memoryProtection)
|
||||||
|
|
||||||
writeCustomIconList()
|
writeCustomIconList()
|
||||||
|
|
||||||
writeObject(DatabaseKDBXXML.ElemRecycleBinEnabled, mDatabaseKDBX.isRecycleBinEnabled)
|
writeBoolean(DatabaseKDBXXML.ElemRecycleBinEnabled, mDatabaseKDBX.isRecycleBinEnabled)
|
||||||
writeUuid(DatabaseKDBXXML.ElemRecycleBinUuid, mDatabaseKDBX.recycleBinUUID)
|
writeUuid(DatabaseKDBXXML.ElemRecycleBinUuid, mDatabaseKDBX.recycleBinUUID)
|
||||||
writeObject(DatabaseKDBXXML.ElemRecycleBinChanged, mDatabaseKDBX.recycleBinChanged)
|
writeDateInstant(DatabaseKDBXXML.ElemRecycleBinChanged, mDatabaseKDBX.recycleBinChanged)
|
||||||
writeUuid(DatabaseKDBXXML.ElemEntryTemplatesGroup, mDatabaseKDBX.entryTemplatesGroup)
|
writeUuid(DatabaseKDBXXML.ElemEntryTemplatesGroup, mDatabaseKDBX.entryTemplatesGroup)
|
||||||
writeObject(DatabaseKDBXXML.ElemEntryTemplatesGroupChanged, mDatabaseKDBX.entryTemplatesGroupChanged.date)
|
writeDateInstant(DatabaseKDBXXML.ElemEntryTemplatesGroupChanged, mDatabaseKDBX.entryTemplatesGroupChanged)
|
||||||
writeObject(DatabaseKDBXXML.ElemHistoryMaxItems, mDatabaseKDBX.historyMaxItems.toLong())
|
writeLong(DatabaseKDBXXML.ElemHistoryMaxItems, mDatabaseKDBX.historyMaxItems.toLong())
|
||||||
writeObject(DatabaseKDBXXML.ElemHistoryMaxSize, mDatabaseKDBX.historyMaxSize)
|
writeLong(DatabaseKDBXXML.ElemHistoryMaxSize, mDatabaseKDBX.historyMaxSize)
|
||||||
writeUuid(DatabaseKDBXXML.ElemLastSelectedGroup, mDatabaseKDBX.lastSelectedGroupUUID)
|
writeUuid(DatabaseKDBXXML.ElemLastSelectedGroup, mDatabaseKDBX.lastSelectedGroupUUID)
|
||||||
writeUuid(DatabaseKDBXXML.ElemLastTopVisibleGroup, mDatabaseKDBX.lastTopVisibleGroupUUID)
|
writeUuid(DatabaseKDBXXML.ElemLastTopVisibleGroup, mDatabaseKDBX.lastTopVisibleGroupUUID)
|
||||||
|
|
||||||
// Seem to work properly if always in meta
|
// Seem to work properly if always in meta
|
||||||
if (header!!.version.isBefore(FILE_VERSION_32_4))
|
if (header!!.version.isBefore(FILE_VERSION_40))
|
||||||
writeMetaBinaries()
|
writeMetaBinaries()
|
||||||
|
|
||||||
writeCustomData(mDatabaseKDBX.customData)
|
writeCustomData(mDatabaseKDBX.customData)
|
||||||
@@ -309,7 +311,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
Log.e(TAG, "Unable to retrieve header", unknownKDF)
|
Log.e(TAG, "Unable to retrieve header", unknownKDF)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.version.isBefore(FILE_VERSION_32_4)) {
|
if (header.version.isBefore(FILE_VERSION_40)) {
|
||||||
header.innerRandomStream = CrsAlgorithm.Salsa20
|
header.innerRandomStream = CrsAlgorithm.Salsa20
|
||||||
header.innerRandomStreamKey = ByteArray(32)
|
header.innerRandomStreamKey = ByteArray(32)
|
||||||
} else {
|
} else {
|
||||||
@@ -324,7 +326,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
throw DatabaseOutputException(e)
|
throw DatabaseOutputException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.version.isBefore(FILE_VERSION_32_4)) {
|
if (header.version.isBefore(FILE_VERSION_40)) {
|
||||||
random.nextBytes(header.streamStartBytes)
|
random.nextBytes(header.streamStartBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,19 +355,21 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
private fun startGroup(group: GroupKDBX) {
|
private fun startGroup(group: GroupKDBX) {
|
||||||
xml.startTag(null, DatabaseKDBXXML.ElemGroup)
|
xml.startTag(null, DatabaseKDBXXML.ElemGroup)
|
||||||
writeUuid(DatabaseKDBXXML.ElemUuid, group.id)
|
writeUuid(DatabaseKDBXXML.ElemUuid, group.id)
|
||||||
writeObject(DatabaseKDBXXML.ElemName, group.title)
|
writeString(DatabaseKDBXXML.ElemName, group.title)
|
||||||
writeObject(DatabaseKDBXXML.ElemNotes, group.notes)
|
writeString(DatabaseKDBXXML.ElemNotes, group.notes)
|
||||||
writeObject(DatabaseKDBXXML.ElemIcon, group.icon.standard.id.toLong())
|
writeLong(DatabaseKDBXXML.ElemIcon, group.icon.standard.id.toLong())
|
||||||
|
|
||||||
if (!group.icon.custom.isUnknown) {
|
if (!group.icon.custom.isUnknown) {
|
||||||
writeUuid(DatabaseKDBXXML.ElemCustomIconID, group.icon.custom.uuid)
|
writeUuid(DatabaseKDBXXML.ElemCustomIconID, group.icon.custom.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeTags(group.tags)
|
||||||
|
writePreviousParentGroup(group.previousParentGroup)
|
||||||
writeTimes(group)
|
writeTimes(group)
|
||||||
writeObject(DatabaseKDBXXML.ElemIsExpanded, group.isExpanded)
|
writeBoolean(DatabaseKDBXXML.ElemIsExpanded, group.isExpanded)
|
||||||
writeObject(DatabaseKDBXXML.ElemGroupDefaultAutoTypeSeq, group.defaultAutoTypeSequence)
|
writeString(DatabaseKDBXXML.ElemGroupDefaultAutoTypeSeq, group.defaultAutoTypeSequence)
|
||||||
writeObject(DatabaseKDBXXML.ElemEnableAutoType, group.enableAutoType)
|
writeBoolean(DatabaseKDBXXML.ElemEnableAutoType, group.enableAutoType)
|
||||||
writeObject(DatabaseKDBXXML.ElemEnableSearching, group.enableSearching)
|
writeBoolean(DatabaseKDBXXML.ElemEnableSearching, group.enableSearching)
|
||||||
writeUuid(DatabaseKDBXXML.ElemLastTopVisibleEntry, group.lastTopVisibleEntry)
|
writeUuid(DatabaseKDBXXML.ElemLastTopVisibleEntry, group.lastTopVisibleEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,24 +384,26 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
xml.startTag(null, DatabaseKDBXXML.ElemEntry)
|
xml.startTag(null, DatabaseKDBXXML.ElemEntry)
|
||||||
|
|
||||||
writeUuid(DatabaseKDBXXML.ElemUuid, entry.id)
|
writeUuid(DatabaseKDBXXML.ElemUuid, entry.id)
|
||||||
writeObject(DatabaseKDBXXML.ElemIcon, entry.icon.standard.id.toLong())
|
writeLong(DatabaseKDBXXML.ElemIcon, entry.icon.standard.id.toLong())
|
||||||
|
|
||||||
if (!entry.icon.custom.isUnknown) {
|
if (!entry.icon.custom.isUnknown) {
|
||||||
writeUuid(DatabaseKDBXXML.ElemCustomIconID, entry.icon.custom.uuid)
|
writeUuid(DatabaseKDBXXML.ElemCustomIconID, entry.icon.custom.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeObject(DatabaseKDBXXML.ElemFgColor, entry.foregroundColor)
|
writeString(DatabaseKDBXXML.ElemFgColor, entry.foregroundColor)
|
||||||
writeObject(DatabaseKDBXXML.ElemBgColor, entry.backgroundColor)
|
writeString(DatabaseKDBXXML.ElemBgColor, entry.backgroundColor)
|
||||||
writeObject(DatabaseKDBXXML.ElemOverrideUrl, entry.overrideURL)
|
writeString(DatabaseKDBXXML.ElemOverrideUrl, entry.overrideURL)
|
||||||
writeObject(DatabaseKDBXXML.ElemTags, entry.tags)
|
|
||||||
|
|
||||||
|
// Write quality check only if false
|
||||||
|
if (!entry.qualityCheck) {
|
||||||
|
writeBoolean(DatabaseKDBXXML.ElemQualityCheck, entry.qualityCheck)
|
||||||
|
}
|
||||||
|
writeTags(entry.tags)
|
||||||
|
writePreviousParentGroup(entry.previousParentGroup)
|
||||||
writeTimes(entry)
|
writeTimes(entry)
|
||||||
|
|
||||||
writeFields(entry.fields)
|
writeFields(entry.fields)
|
||||||
writeEntryBinaries(entry.binaries)
|
writeEntryBinaries(entry.binaries)
|
||||||
if (entry.containsCustomData()) {
|
writeCustomData(entry.customData)
|
||||||
writeCustomData(entry.customData)
|
|
||||||
}
|
|
||||||
writeAutoType(entry.autoType)
|
writeAutoType(entry.autoType)
|
||||||
|
|
||||||
if (!isHistory) {
|
if (!isHistory) {
|
||||||
@@ -408,7 +414,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeObject(name: String, value: String, filterXmlChars: Boolean = false) {
|
private fun writeString(name: String, value: String, filterXmlChars: Boolean = false) {
|
||||||
var xmlString = value
|
var xmlString = value
|
||||||
|
|
||||||
xml.startTag(null, name)
|
xml.startTag(null, name)
|
||||||
@@ -422,38 +428,37 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeObject(name: String, value: Date) {
|
private fun writeDateInstant(name: String, value: DateInstant) {
|
||||||
if (header!!.version.isBefore(FILE_VERSION_32_4)) {
|
val date = value.date
|
||||||
writeObject(name, DatabaseKDBXXML.DateFormatter.format(value))
|
if (header!!.version.isBefore(FILE_VERSION_40)) {
|
||||||
|
writeString(name, DatabaseKDBXXML.DateFormatter.format(date))
|
||||||
} else {
|
} else {
|
||||||
val dt = DateTime(value)
|
val buf = longTo8Bytes(DateKDBXUtil.convertDateToKDBX4Time(DateTime(date)))
|
||||||
val seconds = DateKDBXUtil.convertDateToKDBX4Time(dt)
|
|
||||||
val buf = longTo8Bytes(seconds)
|
|
||||||
val b64 = String(Base64.encode(buf, BASE_64_FLAG))
|
val b64 = String(Base64.encode(buf, BASE_64_FLAG))
|
||||||
writeObject(name, b64)
|
writeString(name, b64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeObject(name: String, value: Long) {
|
private fun writeLong(name: String, value: Long) {
|
||||||
writeObject(name, value.toString())
|
writeString(name, value.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeObject(name: String, value: Boolean?) {
|
private fun writeBoolean(name: String, value: Boolean?) {
|
||||||
val text: String = when {
|
val text: String = when {
|
||||||
value == null -> DatabaseKDBXXML.ValNull
|
value == null -> DatabaseKDBXXML.ValNull
|
||||||
value -> DatabaseKDBXXML.ValTrue
|
value -> DatabaseKDBXXML.ValTrue
|
||||||
else -> DatabaseKDBXXML.ValFalse
|
else -> DatabaseKDBXXML.ValFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
writeObject(name, text)
|
writeString(name, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeUuid(name: String, uuid: UUID) {
|
private fun writeUuid(name: String, uuid: UUID) {
|
||||||
val data = uuidTo16Bytes(uuid)
|
val data = uuidTo16Bytes(uuid)
|
||||||
writeObject(name, String(Base64.encode(data, BASE_64_FLAG)))
|
writeString(name, String(Base64.encode(data, BASE_64_FLAG)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -514,34 +519,29 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
xml.endTag(null, DatabaseKDBXXML.ElemBinaries)
|
xml.endTag(null, DatabaseKDBXXML.ElemBinaries)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
|
||||||
private fun writeObject(name: String, keyName: String, keyValue: String, valueName: String, valueValue: String) {
|
|
||||||
xml.startTag(null, name)
|
|
||||||
|
|
||||||
xml.startTag(null, keyName)
|
|
||||||
xml.text(safeXmlString(keyValue))
|
|
||||||
xml.endTag(null, keyName)
|
|
||||||
|
|
||||||
xml.startTag(null, valueName)
|
|
||||||
xml.text(safeXmlString(valueValue))
|
|
||||||
xml.endTag(null, valueName)
|
|
||||||
|
|
||||||
xml.endTag(null, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeAutoType(autoType: AutoType) {
|
private fun writeAutoType(autoType: AutoType) {
|
||||||
xml.startTag(null, DatabaseKDBXXML.ElemAutoType)
|
xml.startTag(null, DatabaseKDBXXML.ElemAutoType)
|
||||||
|
|
||||||
writeObject(DatabaseKDBXXML.ElemAutoTypeEnabled, autoType.enabled)
|
writeBoolean(DatabaseKDBXXML.ElemAutoTypeEnabled, autoType.enabled)
|
||||||
writeObject(DatabaseKDBXXML.ElemAutoTypeObfuscation, autoType.obfuscationOptions.toKotlinLong())
|
writeLong(DatabaseKDBXXML.ElemAutoTypeObfuscation, autoType.obfuscationOptions.toKotlinLong())
|
||||||
|
|
||||||
if (autoType.defaultSequence.isNotEmpty()) {
|
if (autoType.defaultSequence.isNotEmpty()) {
|
||||||
writeObject(DatabaseKDBXXML.ElemAutoTypeDefaultSeq, autoType.defaultSequence, true)
|
writeString(DatabaseKDBXXML.ElemAutoTypeDefaultSeq, autoType.defaultSequence, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((key, value) in autoType.entrySet()) {
|
for ((key, value) in autoType.entrySet()) {
|
||||||
writeObject(DatabaseKDBXXML.ElemAutoTypeItem, DatabaseKDBXXML.ElemWindow, key, DatabaseKDBXXML.ElemKeystrokeSequence, value)
|
xml.startTag(null, DatabaseKDBXXML.ElemAutoTypeItem)
|
||||||
|
|
||||||
|
xml.startTag(null, DatabaseKDBXXML.ElemWindow)
|
||||||
|
xml.text(safeXmlString(key))
|
||||||
|
xml.endTag(null, DatabaseKDBXXML.ElemWindow)
|
||||||
|
|
||||||
|
xml.startTag(null, DatabaseKDBXXML.ElemKeystrokeSequence)
|
||||||
|
xml.text(safeXmlString(value))
|
||||||
|
xml.endTag(null, DatabaseKDBXXML.ElemKeystrokeSequence)
|
||||||
|
|
||||||
|
xml.endTag(null, DatabaseKDBXXML.ElemAutoTypeItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
xml.endTag(null, DatabaseKDBXXML.ElemAutoType)
|
xml.endTag(null, DatabaseKDBXXML.ElemAutoType)
|
||||||
@@ -592,7 +592,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
xml.startTag(null, DatabaseKDBXXML.ElemDeletedObject)
|
xml.startTag(null, DatabaseKDBXXML.ElemDeletedObject)
|
||||||
|
|
||||||
writeUuid(DatabaseKDBXXML.ElemUuid, value.uuid)
|
writeUuid(DatabaseKDBXXML.ElemUuid, value.uuid)
|
||||||
writeObject(DatabaseKDBXXML.ElemDeletionTime, value.getDeletionTime())
|
writeDateInstant(DatabaseKDBXXML.ElemDeletionTime, value.getDeletionTime())
|
||||||
|
|
||||||
xml.endTag(null, DatabaseKDBXXML.ElemDeletedObject)
|
xml.endTag(null, DatabaseKDBXXML.ElemDeletedObject)
|
||||||
}
|
}
|
||||||
@@ -632,43 +632,72 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
private fun writeMemoryProtection(value: MemoryProtectionConfig) {
|
private fun writeMemoryProtection(value: MemoryProtectionConfig) {
|
||||||
xml.startTag(null, DatabaseKDBXXML.ElemMemoryProt)
|
xml.startTag(null, DatabaseKDBXXML.ElemMemoryProt)
|
||||||
|
|
||||||
writeObject(DatabaseKDBXXML.ElemProtTitle, value.protectTitle)
|
writeBoolean(DatabaseKDBXXML.ElemProtTitle, value.protectTitle)
|
||||||
writeObject(DatabaseKDBXXML.ElemProtUserName, value.protectUserName)
|
writeBoolean(DatabaseKDBXXML.ElemProtUserName, value.protectUserName)
|
||||||
writeObject(DatabaseKDBXXML.ElemProtPassword, value.protectPassword)
|
writeBoolean(DatabaseKDBXXML.ElemProtPassword, value.protectPassword)
|
||||||
writeObject(DatabaseKDBXXML.ElemProtURL, value.protectUrl)
|
writeBoolean(DatabaseKDBXXML.ElemProtURL, value.protectUrl)
|
||||||
writeObject(DatabaseKDBXXML.ElemProtNotes, value.protectNotes)
|
writeBoolean(DatabaseKDBXXML.ElemProtNotes, value.protectNotes)
|
||||||
|
|
||||||
xml.endTag(null, DatabaseKDBXXML.ElemMemoryProt)
|
xml.endTag(null, DatabaseKDBXXML.ElemMemoryProt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeCustomData(customData: Map<String, String>) {
|
private fun writeCustomData(customData: CustomData) {
|
||||||
xml.startTag(null, DatabaseKDBXXML.ElemCustomData)
|
if (customData.isNotEmpty()) {
|
||||||
|
xml.startTag(null, DatabaseKDBXXML.ElemCustomData)
|
||||||
|
|
||||||
for ((key, value) in customData) {
|
customData.doForEachItems { customDataItem ->
|
||||||
writeObject(
|
writeCustomDataItem(customDataItem)
|
||||||
DatabaseKDBXXML.ElemStringDictExItem,
|
}
|
||||||
DatabaseKDBXXML.ElemKey,
|
|
||||||
key,
|
xml.endTag(null, DatabaseKDBXXML.ElemCustomData)
|
||||||
DatabaseKDBXXML.ElemValue,
|
}
|
||||||
value
|
}
|
||||||
)
|
|
||||||
|
private fun writeCustomDataItem(customDataItem: CustomDataItem) {
|
||||||
|
xml.startTag(null, DatabaseKDBXXML.ElemStringDictExItem)
|
||||||
|
|
||||||
|
xml.startTag(null, DatabaseKDBXXML.ElemKey)
|
||||||
|
xml.text(safeXmlString(customDataItem.key))
|
||||||
|
xml.endTag(null, DatabaseKDBXXML.ElemKey)
|
||||||
|
|
||||||
|
xml.startTag(null, DatabaseKDBXXML.ElemValue)
|
||||||
|
xml.text(safeXmlString(customDataItem.value))
|
||||||
|
xml.endTag(null, DatabaseKDBXXML.ElemValue)
|
||||||
|
|
||||||
|
customDataItem.lastModificationTime?.let { lastModificationTime ->
|
||||||
|
writeDateInstant(DatabaseKDBXXML.ElemLastModTime, lastModificationTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
xml.endTag(null, DatabaseKDBXXML.ElemCustomData)
|
xml.endTag(null, DatabaseKDBXXML.ElemStringDictExItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
|
private fun writeTags(tags: Tags) {
|
||||||
|
if (!tags.isEmpty()) {
|
||||||
|
writeString(DatabaseKDBXXML.ElemTags, tags.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
|
private fun writePreviousParentGroup(previousParentGroup: UUID) {
|
||||||
|
if (!header!!.version.isBefore(FILE_VERSION_41)
|
||||||
|
&& previousParentGroup != DatabaseVersioned.UUID_ZERO) {
|
||||||
|
writeUuid(DatabaseKDBXXML.ElemPreviousParentGroup, previousParentGroup)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||||
private fun writeTimes(node: NodeKDBXInterface) {
|
private fun writeTimes(node: NodeKDBXInterface) {
|
||||||
xml.startTag(null, DatabaseKDBXXML.ElemTimes)
|
xml.startTag(null, DatabaseKDBXXML.ElemTimes)
|
||||||
|
|
||||||
writeObject(DatabaseKDBXXML.ElemLastModTime, node.lastModificationTime.date)
|
writeDateInstant(DatabaseKDBXXML.ElemLastModTime, node.lastModificationTime)
|
||||||
writeObject(DatabaseKDBXXML.ElemCreationTime, node.creationTime.date)
|
writeDateInstant(DatabaseKDBXXML.ElemCreationTime, node.creationTime)
|
||||||
writeObject(DatabaseKDBXXML.ElemLastAccessTime, node.lastAccessTime.date)
|
writeDateInstant(DatabaseKDBXXML.ElemLastAccessTime, node.lastAccessTime)
|
||||||
writeObject(DatabaseKDBXXML.ElemExpiryTime, node.expiryTime.date)
|
writeDateInstant(DatabaseKDBXXML.ElemExpiryTime, node.expiryTime)
|
||||||
writeObject(DatabaseKDBXXML.ElemExpires, node.expires)
|
writeBoolean(DatabaseKDBXXML.ElemExpires, node.expires)
|
||||||
writeObject(DatabaseKDBXXML.ElemUsageCount, node.usageCount.toKotlinLong())
|
writeLong(DatabaseKDBXXML.ElemUsageCount, node.usageCount.toKotlinLong())
|
||||||
writeObject(DatabaseKDBXXML.ElemLocationChanged, node.locationChanged.date)
|
writeDateInstant(DatabaseKDBXXML.ElemLocationChanged, node.locationChanged)
|
||||||
|
|
||||||
xml.endTag(null, DatabaseKDBXXML.ElemTimes)
|
xml.endTag(null, DatabaseKDBXXML.ElemTimes)
|
||||||
}
|
}
|
||||||
@@ -709,9 +738,15 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to write custom icon", e)
|
Log.e(TAG, "Unable to write custom icon", e)
|
||||||
} finally {
|
} finally {
|
||||||
writeObject(DatabaseKDBXXML.ElemCustomIconItemData,
|
writeString(DatabaseKDBXXML.ElemCustomIconItemData,
|
||||||
String(Base64.encode(customImageData, BASE_64_FLAG)))
|
String(Base64.encode(customImageData, BASE_64_FLAG)))
|
||||||
}
|
}
|
||||||
|
if (iconCustom.name.isNotEmpty()) {
|
||||||
|
writeString(DatabaseKDBXXML.ElemName, iconCustom.name)
|
||||||
|
}
|
||||||
|
iconCustom.lastModificationTime?.let { lastModificationTime ->
|
||||||
|
writeDateInstant(DatabaseKDBXXML.ElemLastModTime, lastModificationTime)
|
||||||
|
}
|
||||||
|
|
||||||
xml.endTag(null, DatabaseKDBXXML.ElemCustomIconItem)
|
xml.endTag(null, DatabaseKDBXXML.ElemCustomIconItem)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ object ParcelableUtil {
|
|||||||
|
|
||||||
// For writing map with string key to a Parcel
|
// For writing map with string key to a Parcel
|
||||||
fun <V : Parcelable> writeStringParcelableMap(
|
fun <V : Parcelable> writeStringParcelableMap(
|
||||||
parcel: Parcel, flags: Int, map: LinkedHashMap<String, V>) {
|
parcel: Parcel, flags: Int, map: HashMap<String, V>) {
|
||||||
parcel.writeInt(map.size)
|
parcel.writeInt(map.size)
|
||||||
for ((key, value) in map) {
|
for ((key, value) in map) {
|
||||||
parcel.writeString(key)
|
parcel.writeString(key)
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.utils
|
package com.kunzisoft.keepass.utils
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
|
||||||
|
|
||||||
class UnsignedInt(private var unsignedValue: Int) {
|
class UnsignedInt(private var unsignedValue: Int) {
|
||||||
|
|
||||||
constructor(unsignedValue: UnsignedInt) : this(unsignedValue.toKotlinInt())
|
constructor(unsignedValue: UnsignedInt) : this(unsignedValue.toKotlinInt())
|
||||||
|
|||||||
@@ -17,17 +17,42 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<FrameLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/icon_container"
|
android:id="@+id/icon_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="80dp"
|
||||||
android:background="@drawable/background_item_selection">
|
android:background="@drawable/background_item_selection">
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/icon_image"
|
android:id="@+id/icon_image"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/icon_name"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_margin="16dp">
|
android:layout_marginStart="16dp"
|
||||||
</androidx.appcompat.widget.AppCompatImageView>
|
android:layout_marginLeft="16dp"
|
||||||
</FrameLayout>
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/icon_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/icon_image"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingLeft="4dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:paddingRight="4dp" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -22,4 +22,11 @@
|
|||||||
<string name="clipboard_error_title">ক্লিপবোর্ড ত্রুটি</string>
|
<string name="clipboard_error_title">ক্লিপবোর্ড ত্রুটি</string>
|
||||||
<string name="allow">অনুমোদন</string>
|
<string name="allow">অনুমোদন</string>
|
||||||
<string name="file_manager_install_description">ACTION_CREATE_DOCUMENT এবং ACTION_OPEN_DOCUMENT অভিপ্রায় গ্রহণ করে এমন একটি ফাইল ম্যানেজার ডাটাবেস ফাইলগুলো তৈরি করা, খোলা এবং সংরক্ষণ করতে প্রয়োজন।</string>
|
<string name="file_manager_install_description">ACTION_CREATE_DOCUMENT এবং ACTION_OPEN_DOCUMENT অভিপ্রায় গ্রহণ করে এমন একটি ফাইল ম্যানেজার ডাটাবেস ফাইলগুলো তৈরি করা, খোলা এবং সংরক্ষণ করতে প্রয়োজন।</string>
|
||||||
|
<string name="digits">ডিজিট</string>
|
||||||
|
<string name="database">তথ্যভিত্তি</string>
|
||||||
|
<string name="content_description_remove_from_list">সরাও</string>
|
||||||
|
<string name="content_description_update_from_list">হালনাগাদ</string>
|
||||||
|
<string name="discard">বাতিল</string>
|
||||||
|
<string name="validate">সত্যায়ন</string>
|
||||||
|
<string name="content_description_background">পটভূমি</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<string name="add_entry">Přidat záznam</string>
|
<string name="add_entry">Přidat záznam</string>
|
||||||
<string name="add_group">Přidat skupinu</string>
|
<string name="add_group">Přidat skupinu</string>
|
||||||
<string name="encryption_algorithm">Šifrovací algoritmus</string>
|
<string name="encryption_algorithm">Šifrovací algoritmus</string>
|
||||||
<string name="app_timeout">Časový limit</string>
|
<string name="app_timeout">Časový limit překročen</string>
|
||||||
<string name="app_timeout_summary">Doba nečinnosti, po které se aplikace zamkne</string>
|
<string name="app_timeout_summary">Doba nečinnosti, po které se aplikace zamkne</string>
|
||||||
<string name="application">Aplikace</string>
|
<string name="application">Aplikace</string>
|
||||||
<string name="menu_app_settings">Nastavení aplikace</string>
|
<string name="menu_app_settings">Nastavení aplikace</string>
|
||||||
@@ -548,4 +548,19 @@
|
|||||||
<string name="error_upload_file">Během nahrávání souboru došlo k chybě.</string>
|
<string name="error_upload_file">Během nahrávání souboru došlo k chybě.</string>
|
||||||
<string name="error_file_to_big">Soubor, který se pokoušíte nahrát, je příliš velký.</string>
|
<string name="error_file_to_big">Soubor, který se pokoušíte nahrát, je příliš velký.</string>
|
||||||
<string name="content_description_otp_information">Info o jednorázovém hesle</string>
|
<string name="content_description_otp_information">Info o jednorázovém hesle</string>
|
||||||
|
<string name="properties">Vlastnosti</string>
|
||||||
|
<string name="error_export_app_properties">Během exportu vlastností aplikace došlo k chybě</string>
|
||||||
|
<string name="success_export_app_properties">Vlastnosti aplikace byly exportovány</string>
|
||||||
|
<string name="error_import_app_properties">Během importu vlastností aplikace došlo k chybě</string>
|
||||||
|
<string name="success_import_app_properties">Vlastnosti aplikace byly importovány</string>
|
||||||
|
<string name="description_app_properties">Vlastnosti KeePassDX pro správu aplikačních nastavení</string>
|
||||||
|
<string name="export_app_properties_summary">Pro export vlastností aplikace založte soubor</string>
|
||||||
|
<string name="export_app_properties_title">Exportovat vlastnosti aplikace</string>
|
||||||
|
<string name="import_app_properties_summary">Pro import vlastostí aplikace zvolte soubor</string>
|
||||||
|
<string name="import_app_properties_title">Importovat vlastnosti aplikace</string>
|
||||||
|
<string name="error_start_database_action">Během akce v databázi došlo k chybě.</string>
|
||||||
|
<string name="error_remove_file">Při odstraňování dat soboru došlo k chybě.</string>
|
||||||
|
<string name="error_duplicate_file">Datový soubor již existuje.</string>
|
||||||
|
<string name="error_move_group_here">Sem skupinu přesunout nemůžete.</string>
|
||||||
|
<string name="error_word_reserved">Toto slovo je rezervováno a nelze je použít.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -30,17 +30,17 @@
|
|||||||
<string name="add_entry">Eintrag hinzufügen</string>
|
<string name="add_entry">Eintrag hinzufügen</string>
|
||||||
<string name="add_group">Gruppe hinzufügen</string>
|
<string name="add_group">Gruppe hinzufügen</string>
|
||||||
<string name="encryption_algorithm">Verschlüsselungsalgorithmus</string>
|
<string name="encryption_algorithm">Verschlüsselungsalgorithmus</string>
|
||||||
<string name="app_timeout">Zeitüberschreitung</string>
|
<string name="app_timeout">Inaktivitätszeit</string>
|
||||||
<string name="app_timeout_summary">Inaktivitätszeit vor dem Sperren der Datenbank</string>
|
<string name="app_timeout_summary">Zeit bis zum Sperren der Datenbank</string>
|
||||||
<string name="application">App</string>
|
<string name="application">App</string>
|
||||||
<string name="menu_app_settings">App-Einstellungen</string>
|
<string name="menu_app_settings">Einstellungen</string>
|
||||||
<string name="brackets">Klammern</string>
|
<string name="brackets">Klammern</string>
|
||||||
<string name="file_manager_install_description">Zum Erstellen, Öffnen und Speichern von Datenbankdateien wird ein Dateimanager benötigt, der die beabsichtigte Aktion ACTION_CREATE_DOCUMENT und ACTION_OPEN_DOCUMENT akzeptiert.</string>
|
<string name="file_manager_install_description">Zum Erstellen, Öffnen und Speichern von Datenbankdateien wird ein Dateimanager benötigt, der die beabsichtigte Aktion ACTION_CREATE_DOCUMENT und ACTION_OPEN_DOCUMENT akzeptiert.</string>
|
||||||
<string name="clipboard_cleared">Zwischenablage geleert</string>
|
<string name="clipboard_cleared">Zwischenablage geleert</string>
|
||||||
<string name="clipboard_error_title">Zwischenablage-Fehler</string>
|
<string name="clipboard_error_title">Zwischenablage-Fehler</string>
|
||||||
<string name="clipboard_error">Einige Geräte lassen keine Nutzung der Zwischenablage durch Apps zu.</string>
|
<string name="clipboard_error">Einige Geräte lassen keine Nutzung der Zwischenablage durch Apps zu.</string>
|
||||||
<string name="clipboard_error_clear">Leeren der Zwischenablage fehlgeschlagen</string>
|
<string name="clipboard_error_clear">Leeren der Zwischenablage fehlgeschlagen</string>
|
||||||
<string name="clipboard_timeout">Zwischenablage-Timeout</string>
|
<string name="clipboard_timeout">Zwischenablage-Inaktivitätszeit</string>
|
||||||
<string name="clipboard_timeout_summary">Dauer der Speicherung in der Zwischenablage (falls vom Gerät unterstützt)</string>
|
<string name="clipboard_timeout_summary">Dauer der Speicherung in der Zwischenablage (falls vom Gerät unterstützt)</string>
|
||||||
<string name="select_to_copy">%1$s in die Zwischenablage kopieren</string>
|
<string name="select_to_copy">%1$s in die Zwischenablage kopieren</string>
|
||||||
<string name="retrieving_db_key">Datenbank-Schlüsseldatei erzeugen …</string>
|
<string name="retrieving_db_key">Datenbank-Schlüsseldatei erzeugen …</string>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
<string name="file_not_found_content">Datei nicht gefunden. Bitte versuchen, sie über den Dateimanager zu öffnen.</string>
|
<string name="file_not_found_content">Datei nicht gefunden. Bitte versuchen, sie über den Dateimanager zu öffnen.</string>
|
||||||
<string name="file_browser">Dateimanager</string>
|
<string name="file_browser">Dateimanager</string>
|
||||||
<string name="generate_password">Passwort generieren</string>
|
<string name="generate_password">Passwort generieren</string>
|
||||||
<string name="hint_conf_pass">Passwort wiederholen</string>
|
<string name="hint_conf_pass">Passwort bestätigen</string>
|
||||||
<string name="hint_generated_password">Generiertes Passwort</string>
|
<string name="hint_generated_password">Generiertes Passwort</string>
|
||||||
<string name="hint_group_name">Name der Gruppe</string>
|
<string name="hint_group_name">Name der Gruppe</string>
|
||||||
<string name="hint_keyfile">Schlüsseldatei</string>
|
<string name="hint_keyfile">Schlüsseldatei</string>
|
||||||
@@ -104,14 +104,14 @@
|
|||||||
<string name="about">Über</string>
|
<string name="about">Über</string>
|
||||||
<string name="menu_change_key_settings">Hauptschlüssel ändern</string>
|
<string name="menu_change_key_settings">Hauptschlüssel ändern</string>
|
||||||
<string name="settings">Einstellungen</string>
|
<string name="settings">Einstellungen</string>
|
||||||
<string name="menu_database_settings">Datenbank-Einstellungen</string>
|
<string name="menu_database_settings">Datenbankeinstellungen</string>
|
||||||
<string name="menu_delete">Löschen</string>
|
<string name="menu_delete">Löschen</string>
|
||||||
<string name="menu_donate">Spenden</string>
|
<string name="menu_donate">Spenden</string>
|
||||||
<string name="menu_edit">Bearbeiten</string>
|
<string name="menu_edit">Bearbeiten</string>
|
||||||
<string name="menu_hide_password">Passwort verstecken</string>
|
<string name="menu_hide_password">Passwort verstecken</string>
|
||||||
<string name="menu_lock">Datenbank sperren</string>
|
<string name="menu_lock">Datenbank sperren</string>
|
||||||
<string name="menu_open">Öffnen</string>
|
<string name="menu_open">Öffnen</string>
|
||||||
<string name="menu_search">Suchen</string>
|
<string name="menu_search">Suche</string>
|
||||||
<string name="menu_showpass">Passwort anzeigen</string>
|
<string name="menu_showpass">Passwort anzeigen</string>
|
||||||
<string name="menu_url">URL öffnen</string>
|
<string name="menu_url">URL öffnen</string>
|
||||||
<string name="minus">Bindestrich</string>
|
<string name="minus">Bindestrich</string>
|
||||||
@@ -202,14 +202,14 @@
|
|||||||
<string name="autofill_service_name">KeePassDX autom. Formularausfüllung</string>
|
<string name="autofill_service_name">KeePassDX autom. Formularausfüllung</string>
|
||||||
<string name="autofill_sign_in_prompt">Mit KeePassDX anmelden</string>
|
<string name="autofill_sign_in_prompt">Mit KeePassDX anmelden</string>
|
||||||
<string name="set_autofill_service_title">Standarddienst für automatisches Ausfüllen festlegen</string>
|
<string name="set_autofill_service_title">Standarddienst für automatisches Ausfüllen festlegen</string>
|
||||||
<string name="autofill_explanation_summary">Automatisches Ausfüllen aktivieren, um schnell Formulare in anderen Apps auszufüllen</string>
|
<string name="autofill_explanation_summary">Automatisches Ausfüllen aktivieren, um Formulare schnell in anderen Apps auszufüllen</string>
|
||||||
<string name="clipboard">Zwischenablage</string>
|
<string name="clipboard">Zwischenablage</string>
|
||||||
<string name="biometric_delete_all_key_title">Verschlüsselungsschlüssel löschen</string>
|
<string name="biometric_delete_all_key_title">Verschlüsselungsschlüssel löschen</string>
|
||||||
<string name="biometric_delete_all_key_summary">Lösche alle Verschlüsselungsschlüssel, die mit der erweiterten Entsperrerkennung zusammenhängen</string>
|
<string name="biometric_delete_all_key_summary">Alle Verschlüsselungsschlüssel löschen, die mit der modernen Entsperrerkennung zusammenhängen</string>
|
||||||
<string name="unavailable_feature_version">Auf dem Gerät läuft Android %1$s, eine Version ab %2$s wäre aber nötig.</string>
|
<string name="unavailable_feature_version">Auf dem Gerät läuft Android %1$s, eine Version ab %2$s wäre aber nötig.</string>
|
||||||
<string name="unavailable_feature_hardware">Keine entsprechende Hardware.</string>
|
<string name="unavailable_feature_hardware">Keine entsprechende Hardware.</string>
|
||||||
<string name="recycle_bin_title">Papierkorb-Nutzung</string>
|
<string name="recycle_bin_title">Papierkorb-Nutzung</string>
|
||||||
<string name="recycle_bin_summary">Verschiebt Gruppen oder Einträge in den Papierkorb, bevor sie gelöscht werden.</string>
|
<string name="recycle_bin_summary">Verschiebt Gruppen oder Einträge in den Papierkorb, bevor sie gelöscht werden</string>
|
||||||
<string name="monospace_font_fields_enable_title">Feldschriftart</string>
|
<string name="monospace_font_fields_enable_title">Feldschriftart</string>
|
||||||
<string name="monospace_font_fields_enable_summary">Schriftart in Feldern ändern, um Lesbarkeit zu verbessern</string>
|
<string name="monospace_font_fields_enable_summary">Schriftart in Feldern ändern, um Lesbarkeit zu verbessern</string>
|
||||||
<string name="allow_copy_password_title">Zwischenablage vertrauen</string>
|
<string name="allow_copy_password_title">Zwischenablage vertrauen</string>
|
||||||
@@ -218,8 +218,8 @@
|
|||||||
<string name="database_description_title">Datenbankbeschreibung</string>
|
<string name="database_description_title">Datenbankbeschreibung</string>
|
||||||
<string name="database_version_title">Datenbankversion</string>
|
<string name="database_version_title">Datenbankversion</string>
|
||||||
<string name="text_appearance">Text</string>
|
<string name="text_appearance">Text</string>
|
||||||
<string name="application_appearance">Interface</string>
|
<string name="application_appearance">Benutzeroberfläche</string>
|
||||||
<string name="other">Andere</string>
|
<string name="other">Sonstiges</string>
|
||||||
<string name="keyboard">Tastatur</string>
|
<string name="keyboard">Tastatur</string>
|
||||||
<string name="magic_keyboard_title">Magikeyboard</string>
|
<string name="magic_keyboard_title">Magikeyboard</string>
|
||||||
<string name="magic_keyboard_explanation_summary">Eine eigene Tastatur zum einfachen Ausfüllen aller Passwort- und Identitätsfelder aktivieren</string>
|
<string name="magic_keyboard_explanation_summary">Eine eigene Tastatur zum einfachen Ausfüllen aller Passwort- und Identitätsfelder aktivieren</string>
|
||||||
@@ -297,8 +297,8 @@
|
|||||||
<string name="keyboard_label">Magikeyboard (KeePassDX)</string>
|
<string name="keyboard_label">Magikeyboard (KeePassDX)</string>
|
||||||
<string name="keyboard_setting_label">Magikeyboard-Einstellungen</string>
|
<string name="keyboard_setting_label">Magikeyboard-Einstellungen</string>
|
||||||
<string name="keyboard_entry_category">Eintrag</string>
|
<string name="keyboard_entry_category">Eintrag</string>
|
||||||
<string name="keyboard_entry_timeout_title">Timeout</string>
|
<string name="keyboard_entry_timeout_title">Inaktivitätszeit</string>
|
||||||
<string name="keyboard_entry_timeout_summary">Timeout zum Löschen der Tastatureingabe</string>
|
<string name="keyboard_entry_timeout_summary">Zeit bis zum Löschen der Tastatureingabe</string>
|
||||||
<string name="keyboard_notification_entry_title">Benachrichtigung</string>
|
<string name="keyboard_notification_entry_title">Benachrichtigung</string>
|
||||||
<string name="keyboard_notification_entry_summary">Benachrichtigung anzeigen, wenn ein Eintrag abrufbar ist</string>
|
<string name="keyboard_notification_entry_summary">Benachrichtigung anzeigen, wenn ein Eintrag abrufbar ist</string>
|
||||||
<string name="keyboard_notification_entry_content_title_text">Eintrag</string>
|
<string name="keyboard_notification_entry_content_title_text">Eintrag</string>
|
||||||
@@ -384,8 +384,8 @@
|
|||||||
<string name="error_otp_digits">Token muss %1$d bis %2$d Stellen enthalten.</string>
|
<string name="error_otp_digits">Token muss %1$d bis %2$d Stellen enthalten.</string>
|
||||||
<string name="invalid_db_same_uuid">%1$s mit derselben UUID %2$s existiert bereits.</string>
|
<string name="invalid_db_same_uuid">%1$s mit derselben UUID %2$s existiert bereits.</string>
|
||||||
<string name="creating_database">Datenbank wird erstellt …</string>
|
<string name="creating_database">Datenbank wird erstellt …</string>
|
||||||
<string name="menu_security_settings">Sicherheits-Einstellungen</string>
|
<string name="menu_security_settings">Sicherheitseinstellungen</string>
|
||||||
<string name="menu_master_key_settings">Hauptschlüssel-Einstellungen</string>
|
<string name="menu_master_key_settings">Hauptschlüsseleinstellungen</string>
|
||||||
<string name="contains_duplicate_uuid">Die Datenbank enthält UUID-Duplikate.</string>
|
<string name="contains_duplicate_uuid">Die Datenbank enthält UUID-Duplikate.</string>
|
||||||
<string name="contains_duplicate_uuid_procedure">Problem lösen, indem neue UUIDs für Duplikate generiert werden und danach fortfahren\?</string>
|
<string name="contains_duplicate_uuid_procedure">Problem lösen, indem neue UUIDs für Duplikate generiert werden und danach fortfahren\?</string>
|
||||||
<string name="database_opened">Datenbank geöffnet</string>
|
<string name="database_opened">Datenbank geöffnet</string>
|
||||||
@@ -403,8 +403,8 @@
|
|||||||
<string name="settings_database_force_changing_master_key_summary">Ändern des Hauptschlüssels erforderlich (Tage)</string>
|
<string name="settings_database_force_changing_master_key_summary">Ändern des Hauptschlüssels erforderlich (Tage)</string>
|
||||||
<string name="settings_database_force_changing_master_key_next_time_title">Erneuerung beim nächsten Mal erzwingen</string>
|
<string name="settings_database_force_changing_master_key_next_time_title">Erneuerung beim nächsten Mal erzwingen</string>
|
||||||
<string name="settings_database_force_changing_master_key_next_time_summary">Änderung des Hauptschlüssels beim nächsten Mal erfordern (einmalig)</string>
|
<string name="settings_database_force_changing_master_key_next_time_summary">Änderung des Hauptschlüssels beim nächsten Mal erfordern (einmalig)</string>
|
||||||
<string name="database_default_username_title">Vorgegebener Benutzername</string>
|
<string name="database_default_username_title">Standard-Benutzername</string>
|
||||||
<string name="database_custom_color_title">Benutzerdefinierte Datenbankfarbe</string>
|
<string name="database_custom_color_title">Eigene Datenbankfarbe</string>
|
||||||
<string name="compression">Kompression</string>
|
<string name="compression">Kompression</string>
|
||||||
<string name="compression_none">Keine</string>
|
<string name="compression_none">Keine</string>
|
||||||
<string name="compression_gzip">Gzip</string>
|
<string name="compression_gzip">Gzip</string>
|
||||||
@@ -431,7 +431,7 @@
|
|||||||
<string name="hide_expired_entries_title">Abgelaufene Einträge ausblenden</string>
|
<string name="hide_expired_entries_title">Abgelaufene Einträge ausblenden</string>
|
||||||
<string name="hide_expired_entries_summary">Abgelaufene Einträge werden nicht angezeigt</string>
|
<string name="hide_expired_entries_summary">Abgelaufene Einträge werden nicht angezeigt</string>
|
||||||
<string name="style_choose_title">App-Design</string>
|
<string name="style_choose_title">App-Design</string>
|
||||||
<string name="style_choose_summary">Design, das in der App genutzt wird</string>
|
<string name="style_choose_summary">In der App verwendetes Design</string>
|
||||||
<string-array name="list_style_names">
|
<string-array name="list_style_names">
|
||||||
<item>Wald</item>
|
<item>Wald</item>
|
||||||
<item>Göttlich</item>
|
<item>Göttlich</item>
|
||||||
@@ -448,7 +448,7 @@
|
|||||||
<string name="discard">Verwerfen</string>
|
<string name="discard">Verwerfen</string>
|
||||||
<string name="discard_changes">Änderungen verwerfen\?</string>
|
<string name="discard_changes">Änderungen verwerfen\?</string>
|
||||||
<string name="validate">Validieren</string>
|
<string name="validate">Validieren</string>
|
||||||
<string name="autofill_auto_search_summary">Automatisch Suchergebnisse nach Web-Domain oder Anwendungs-ID vorschlagen</string>
|
<string name="autofill_auto_search_summary">Suchergebnisse automatisch nach Web-Domain oder Anwendungs-ID vorschlagen</string>
|
||||||
<string name="autofill_auto_search_title">Automatische Suche</string>
|
<string name="autofill_auto_search_title">Automatische Suche</string>
|
||||||
<string name="lock_database_show_button_summary">Zeigt die Sperrtaste in der Benutzeroberfläche an</string>
|
<string name="lock_database_show_button_summary">Zeigt die Sperrtaste in der Benutzeroberfläche an</string>
|
||||||
<string name="lock_database_show_button_title">Sperrtaste anzeigen</string>
|
<string name="lock_database_show_button_title">Sperrtaste anzeigen</string>
|
||||||
@@ -502,11 +502,11 @@
|
|||||||
<string name="save_mode">Speichermodus</string>
|
<string name="save_mode">Speichermodus</string>
|
||||||
<string name="search_mode">Suchmodus</string>
|
<string name="search_mode">Suchmodus</string>
|
||||||
<string name="error_registration_read_only">Das Speichern eines neuen Elements in einer schreibgeschützten Datenbank ist nicht zulässig</string>
|
<string name="error_registration_read_only">Das Speichern eines neuen Elements in einer schreibgeschützten Datenbank ist nicht zulässig</string>
|
||||||
<string name="autofill_save_search_info_summary">Versuche bei manueller Eingabeauswahl gemeinsam genutzte Informationen zu speichern</string>
|
<string name="autofill_save_search_info_summary">Suchdaten bei manueller Auswahl einer Eingabe wenn möglich speichern</string>
|
||||||
<string name="autofill_ask_to_save_data_summary">Speichern von Daten anfordern, wenn ein Formular überprüft wird</string>
|
<string name="autofill_ask_to_save_data_summary">Nachfragen, ob die Daten gespeichert werden sollen, wenn ein Formular ausgefüllt ist</string>
|
||||||
<string name="autofill_ask_to_save_data_title">Speichern von Daten anfordern</string>
|
<string name="autofill_ask_to_save_data_title">Speichern von Daten abfragen</string>
|
||||||
<string name="autofill_save_search_info_title">Suchinformationen speichern</string>
|
<string name="autofill_save_search_info_title">Suchinformationen speichern</string>
|
||||||
<string name="autofill_close_database_summary">Datenbank nach der Auswahl des automatischen Ausfüllens schließen</string>
|
<string name="autofill_close_database_summary">Datenbank nach Auswahl eines Eintrags zum automatischen Ausfüllen schließen</string>
|
||||||
<string name="keyboard_save_search_info_summary">Versuche bei manueller Eingabeauswahl gemeinsam genutzte Informationen zu speichern</string>
|
<string name="keyboard_save_search_info_summary">Versuche bei manueller Eingabeauswahl gemeinsam genutzte Informationen zu speichern</string>
|
||||||
<string name="keyboard_save_search_info_title">Gemeinsam genutzte Informationen speichern</string>
|
<string name="keyboard_save_search_info_title">Gemeinsam genutzte Informationen speichern</string>
|
||||||
<string name="warning_empty_recycle_bin">Sollen alle ausgewählten Knoten wirklich aus dem Papierkorb gelöscht werden\?</string>
|
<string name="warning_empty_recycle_bin">Sollen alle ausgewählten Knoten wirklich aus dem Papierkorb gelöscht werden\?</string>
|
||||||
@@ -517,13 +517,13 @@
|
|||||||
<string name="menu_keystore_remove_key">Schlüssel für modernes Entsperren löschen</string>
|
<string name="menu_keystore_remove_key">Schlüssel für modernes Entsperren löschen</string>
|
||||||
<string name="advanced_unlock_prompt_store_credential_title">Moderne Entsperrerkennung</string>
|
<string name="advanced_unlock_prompt_store_credential_title">Moderne Entsperrerkennung</string>
|
||||||
<string name="education_advanced_unlock_summary">Verknüpfen Sie Ihr Passwort mit Ihren gescannten Biometriedaten oder Daten zur Geräteanmeldung, um schnell Ihre Datenbank zu entsperren.</string>
|
<string name="education_advanced_unlock_summary">Verknüpfen Sie Ihr Passwort mit Ihren gescannten Biometriedaten oder Daten zur Geräteanmeldung, um schnell Ihre Datenbank zu entsperren.</string>
|
||||||
<string name="education_advanced_unlock_title">Erweiterte Entsperrung der Datenbank</string>
|
<string name="education_advanced_unlock_title">Moderne Entsperrung der Datenbank</string>
|
||||||
<string name="advanced_unlock_timeout">Verfallzeit der erweiterten Entsperrung</string>
|
<string name="advanced_unlock_timeout">Verfallzeit der modernen Entsperrung</string>
|
||||||
<string name="temp_advanced_unlock_timeout_summary">Laufzeit der modernen Entsperrung bevor ihr Inhalt gelöscht wird</string>
|
<string name="temp_advanced_unlock_timeout_summary">Laufzeit der modernen Entsperrung bevor ihr Inhalt gelöscht wird</string>
|
||||||
<string name="temp_advanced_unlock_timeout_title">Verfall der modernen Entsperrung</string>
|
<string name="temp_advanced_unlock_timeout_title">Verfall der modernen Entsperrung</string>
|
||||||
<string name="temp_advanced_unlock_enable_summary">Keinen verschlüsselten Inhalt speichern, um moderne Entsperrung zu benutzen</string>
|
<string name="temp_advanced_unlock_enable_summary">Keine zur modernen Entsperrung verwendeten, verschlüsselten Inhalte speichern</string>
|
||||||
<string name="temp_advanced_unlock_enable_title">Temporär moderne Entsperrung</string>
|
<string name="temp_advanced_unlock_enable_title">Befristete moderne Entsperrung</string>
|
||||||
<string name="device_credential_unlock_enable_summary">Erlaubt Ihn die Geräteanmeldedaten zum Öffnen der Datenbank zu verwenden</string>
|
<string name="device_credential_unlock_enable_summary">Ermöglicht das Öffnen der Datenbank mit Ihren Geräte-Anmeldeinformationen</string>
|
||||||
<string name="advanced_unlock_tap_delete">Drücken, um Schlüssel für modernes Entsperren zu löschen</string>
|
<string name="advanced_unlock_tap_delete">Drücken, um Schlüssel für modernes Entsperren zu löschen</string>
|
||||||
<string name="content">Inhalt</string>
|
<string name="content">Inhalt</string>
|
||||||
<string name="advanced_unlock_prompt_extract_credential_title">Datenbank mit moderner Entsperrerkennung öffnen</string>
|
<string name="advanced_unlock_prompt_extract_credential_title">Datenbank mit moderner Entsperrerkennung öffnen</string>
|
||||||
@@ -532,7 +532,7 @@
|
|||||||
<string name="select_entry">Wähle Eintrag</string>
|
<string name="select_entry">Wähle Eintrag</string>
|
||||||
<string name="back_to_previous_keyboard">Zurück zur vorherigen Tastatur</string>
|
<string name="back_to_previous_keyboard">Zurück zur vorherigen Tastatur</string>
|
||||||
<string name="custom_fields">Benutzerdefinierte Felder</string>
|
<string name="custom_fields">Benutzerdefinierte Felder</string>
|
||||||
<string name="advanced_unlock_delete_all_key_warning">Alle Verschlüsselungsschlüssel, die mit der erweiterten Entsperrerkennung zusammenhängen, löschen\?</string>
|
<string name="advanced_unlock_delete_all_key_warning">Alle Verschlüsselungsschlüssel, die mit der modernen Entsperrerkennung zusammenhängen, löschen\?</string>
|
||||||
<string name="device_credential_unlock_enable_title">Geräteanmeldedaten entsperren</string>
|
<string name="device_credential_unlock_enable_title">Geräteanmeldedaten entsperren</string>
|
||||||
<string name="device_credential">Geräteanmeldedaten</string>
|
<string name="device_credential">Geräteanmeldedaten</string>
|
||||||
<string name="credential_before_click_advanced_unlock_button">Geben Sie das Passwort ein und klicken Sie dann auf diesen Knopf.</string>
|
<string name="credential_before_click_advanced_unlock_button">Geben Sie das Passwort ein und klicken Sie dann auf diesen Knopf.</string>
|
||||||
@@ -548,9 +548,9 @@
|
|||||||
<string name="error_file_to_big">Die Datei, die hochgeladen werden soll, ist zu groß.</string>
|
<string name="error_file_to_big">Die Datei, die hochgeladen werden soll, ist zu groß.</string>
|
||||||
<string name="warning_database_info_changed">Die in Ihrer Datenbank enthaltenen Informationen wurden außerhalb der App geändert.</string>
|
<string name="warning_database_info_changed">Die in Ihrer Datenbank enthaltenen Informationen wurden außerhalb der App geändert.</string>
|
||||||
<string name="error_remove_file">Beim Löschen der Datei trat ein Fehler auf.</string>
|
<string name="error_remove_file">Beim Löschen der Datei trat ein Fehler auf.</string>
|
||||||
<string name="error_duplicate_file">Die Datei gibt es bereits.</string>
|
<string name="error_duplicate_file">Die Datei existiert bereits.</string>
|
||||||
<string name="error_upload_file">Beim Hochladen der Datei trat ein Fehler auf.</string>
|
<string name="error_upload_file">Beim Hochladen der Datei trat ein Fehler auf.</string>
|
||||||
<string name="import_app_properties_title">Importieren von Anwendungeneigenschaften</string>
|
<string name="import_app_properties_title">App-Eigenschaften importieren</string>
|
||||||
<string name="error_start_database_action">Beim Ausführen einer Aktion in der Datenbank ist ein Fehler aufgetreten.</string>
|
<string name="error_start_database_action">Beim Ausführen einer Aktion in der Datenbank ist ein Fehler aufgetreten.</string>
|
||||||
<string name="error_otp_type">Der vorhandene Einmalpassworttyp wird von diesem Formular nicht erkannt, seine Validierung erzeugt das Token möglicherweise nicht mehr korrekt.</string>
|
<string name="error_otp_type">Der vorhandene Einmalpassworttyp wird von diesem Formular nicht erkannt, seine Validierung erzeugt das Token möglicherweise nicht mehr korrekt.</string>
|
||||||
<string name="content_description_otp_information">Informationen zu Einmalpasswörtern</string>
|
<string name="content_description_otp_information">Informationen zu Einmalpasswörtern</string>
|
||||||
@@ -559,8 +559,23 @@
|
|||||||
<string name="success_export_app_properties">App-Eigenschaften exportiert</string>
|
<string name="success_export_app_properties">App-Eigenschaften exportiert</string>
|
||||||
<string name="error_import_app_properties">Fehler beim Importieren der App-Eigenschaften</string>
|
<string name="error_import_app_properties">Fehler beim Importieren der App-Eigenschaften</string>
|
||||||
<string name="success_import_app_properties">App-Eigenschaften importiert</string>
|
<string name="success_import_app_properties">App-Eigenschaften importiert</string>
|
||||||
<string name="export_app_properties_summary">Erstellen einer Datei zum Exportieren von App-Eigenschaften</string>
|
<string name="export_app_properties_summary">Datei zum Exportieren von App-Eigenschaften erstellen</string>
|
||||||
<string name="export_app_properties_title">App-Eigenschaften exportieren</string>
|
<string name="export_app_properties_title">App-Eigenschaften exportieren</string>
|
||||||
<string name="import_app_properties_summary">Wählen Sie eine Datei zum Importieren von App-Eigenschaften</string>
|
<string name="import_app_properties_summary">Datei zum Importieren von App-Eigenschaften auswählen</string>
|
||||||
<string name="error_move_group_here">Sie können hier keine Gruppe verschieben.</string>
|
<string name="error_move_group_here">Sie können hier keine Gruppe verschieben.</string>
|
||||||
|
<string name="autofill_inline_suggestions_title">Ausfüllvorschläge</string>
|
||||||
|
<string name="error_word_reserved">Dieses Wort ist reserviert und kann nicht verwendet werden.</string>
|
||||||
|
<string name="icon_section_custom">Benutzerdefiniert</string>
|
||||||
|
<string name="icon_section_standard">Standard</string>
|
||||||
|
<string name="style_brightness_summary">Helles oder dunkles Design auswählen</string>
|
||||||
|
<string name="style_brightness_title">Designhelligkeit</string>
|
||||||
|
<string name="unit_gibibyte">GiB</string>
|
||||||
|
<string name="unit_mebibyte">MiB</string>
|
||||||
|
<string name="unit_kibibyte">KiB</string>
|
||||||
|
<string name="unit_byte">B</string>
|
||||||
|
<string name="download_canceled">Abgebrochen!</string>
|
||||||
|
<string name="autofill_inline_suggestions_keyboard">Vorschläge für automatisches Ausfüllen hinzugefügt.</string>
|
||||||
|
<string name="autofill_inline_suggestions_summary">Wenn möglich unmittelbar Vorschläge zum automatischen Ausfüllen auf einer kompatiblen Tastatur anzeigen</string>
|
||||||
|
<string name="properties">Eigenschaften</string>
|
||||||
|
<string name="description_app_properties">KeePassDX-Eigenschaften zur Verwaltung der App-Einstellungen</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -367,7 +367,7 @@
|
|||||||
<string name="contribution">Doprinos</string>
|
<string name="contribution">Doprinos</string>
|
||||||
<string name="error_label_exists">Ova oznaka već postoji.</string>
|
<string name="error_label_exists">Ova oznaka već postoji.</string>
|
||||||
<string name="warning_database_read_only">Za spremanje promjena u bazi podataka, datoteci dozvoli pisanje</string>
|
<string name="warning_database_read_only">Za spremanje promjena u bazi podataka, datoteci dozvoli pisanje</string>
|
||||||
<string name="app_timeout">Istek vremena aplikacije</string>
|
<string name="app_timeout">Istek vremena</string>
|
||||||
<string name="content_description_repeat_toggle_password_visibility">Ponovo uklj/isklj vidljivosti lozinke</string>
|
<string name="content_description_repeat_toggle_password_visibility">Ponovo uklj/isklj vidljivosti lozinke</string>
|
||||||
<string name="warning_password_encoding">Izbjegni u lozinkama koristiti znakove koji su izvan formata kodiranja teksta u datoteci baze podataka (neprepoznati znakovi pretvaraju se u isto slovo).</string>
|
<string name="warning_password_encoding">Izbjegni u lozinkama koristiti znakove koji su izvan formata kodiranja teksta u datoteci baze podataka (neprepoznati znakovi pretvaraju se u isto slovo).</string>
|
||||||
<string name="rounds_explanation">Dodatni prolazi šifriranja pružaju veću zaštitu od brutalnih napada, ali stvarno mogu usporiti učitavanje i spremanje.</string>
|
<string name="rounds_explanation">Dodatni prolazi šifriranja pružaju veću zaštitu od brutalnih napada, ali stvarno mogu usporiti učitavanje i spremanje.</string>
|
||||||
@@ -555,4 +555,6 @@
|
|||||||
<string name="import_app_properties_summary">Odaberi datoteku za uvoz svojstva aplikacije</string>
|
<string name="import_app_properties_summary">Odaberi datoteku za uvoz svojstva aplikacije</string>
|
||||||
<string name="import_app_properties_title">Uvezi svojstva aplikacije</string>
|
<string name="import_app_properties_title">Uvezi svojstva aplikacije</string>
|
||||||
<string name="error_start_database_action">Došlo je do greške tijekom izvođenja radnje u bazi podataka.</string>
|
<string name="error_start_database_action">Došlo je do greške tijekom izvođenja radnje u bazi podataka.</string>
|
||||||
|
<string name="error_move_group_here">Grupa se ne može ovdje premjestiti.</string>
|
||||||
|
<string name="error_word_reserved">Ova je riječ rezervirana i ne može se koristiti.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -564,4 +564,5 @@
|
|||||||
<string name="import_app_properties_summary">Seleziona un file da cui importare le proprietà dell\'app</string>
|
<string name="import_app_properties_summary">Seleziona un file da cui importare le proprietà dell\'app</string>
|
||||||
<string name="import_app_properties_title">Importa le proprietà dell\'app</string>
|
<string name="import_app_properties_title">Importa le proprietà dell\'app</string>
|
||||||
<string name="error_word_reserved">Questa parola è riservata e non può essere usata.</string>
|
<string name="error_word_reserved">Questa parola è riservata e non può essere usata.</string>
|
||||||
|
<string name="error_move_group_here">Non è possibile spostare un gruppo qui.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<string name="encryption">暗号化</string>
|
<string name="encryption">暗号化</string>
|
||||||
<string name="encryption_algorithm">暗号化アルゴリズム</string>
|
<string name="encryption_algorithm">暗号化アルゴリズム</string>
|
||||||
<string name="key_derivation_function">鍵導出関数</string>
|
<string name="key_derivation_function">鍵導出関数</string>
|
||||||
<string name="app_timeout">アプリのタイムアウト</string>
|
<string name="app_timeout">タイムアウト</string>
|
||||||
<string name="app_timeout_summary">この期間アプリの操作がなかった場合、データベースをロックします</string>
|
<string name="app_timeout_summary">この期間アプリの操作がなかった場合、データベースをロックします</string>
|
||||||
<string name="application">アプリ</string>
|
<string name="application">アプリ</string>
|
||||||
<string name="brackets">かっこ</string>
|
<string name="brackets">かっこ</string>
|
||||||
@@ -559,4 +559,6 @@
|
|||||||
<string name="export_app_properties_summary">アプリのプロパティをエクスポートするファイルを作成します</string>
|
<string name="export_app_properties_summary">アプリのプロパティをエクスポートするファイルを作成します</string>
|
||||||
<string name="export_app_properties_title">アプリのプロパティをエクスポートする</string>
|
<string name="export_app_properties_title">アプリのプロパティをエクスポートする</string>
|
||||||
<string name="error_start_database_action">データベースに対するアクションの実行中にエラーが発生しました。</string>
|
<string name="error_start_database_action">データベースに対するアクションの実行中にエラーが発生しました。</string>
|
||||||
|
<string name="error_word_reserved">この単語は予約語のため使用できません。</string>
|
||||||
|
<string name="error_move_group_here">グループをここに移動できません。</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -560,4 +560,6 @@
|
|||||||
<string name="import_app_properties_summary">Wybierz plik, aby zaimportować właściwości aplikacji</string>
|
<string name="import_app_properties_summary">Wybierz plik, aby zaimportować właściwości aplikacji</string>
|
||||||
<string name="import_app_properties_title">Importuj właściwości aplikacji</string>
|
<string name="import_app_properties_title">Importuj właściwości aplikacji</string>
|
||||||
<string name="error_start_database_action">Wystąpił błąd podczas wykonywania akcji w bazie danych.</string>
|
<string name="error_start_database_action">Wystąpił błąd podczas wykonywania akcji w bazie danych.</string>
|
||||||
|
<string name="error_move_group_here">Nie możesz przenieść tutaj grupy.</string>
|
||||||
|
<string name="error_word_reserved">To słowo jest zastrzeżone i nie może być używane.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
<string name="field_name">Nome do campo</string>
|
<string name="field_name">Nome do campo</string>
|
||||||
<string name="error_autofill_enable_service">Não pôde ser ativado o serviço de preenchimento automático.</string>
|
<string name="error_autofill_enable_service">Não pôde ser ativado o serviço de preenchimento automático.</string>
|
||||||
<string name="error_wrong_length">Digite um número inteiro positivo no campo \"Tamanho\".</string>
|
<string name="error_wrong_length">Digite um número inteiro positivo no campo \"Tamanho\".</string>
|
||||||
<string name="error_string_key">Um nome do campo é necessário para cada cadeia.</string>
|
<string name="error_string_key">Um nome do campo é necessário para cada string.</string>
|
||||||
<string name="error_rounds_too_large">\"Número de rodadas\" é muito grande. Modificado para 2147483648.</string>
|
<string name="error_rounds_too_large">\"Número de rodadas\" é muito grande. Modificado para 2147483648.</string>
|
||||||
<string name="error_pass_match">As palavras-passe não coincidem.</string>
|
<string name="error_pass_match">As palavras-passe não coincidem.</string>
|
||||||
<string name="error_pass_gen_type">Pelo menos um tipo de geração de palavra-chave deve ser selecionado.</string>
|
<string name="error_pass_gen_type">Pelo menos um tipo de geração de palavra-chave deve ser selecionado.</string>
|
||||||
@@ -318,7 +318,7 @@
|
|||||||
<string name="error_otp_period">O período deve estar entre %1$d e %2$d segundos.</string>
|
<string name="error_otp_period">O período deve estar entre %1$d e %2$d segundos.</string>
|
||||||
<string name="error_otp_counter">O contador deve estar entre %1$d e %2$d.</string>
|
<string name="error_otp_counter">O contador deve estar entre %1$d e %2$d.</string>
|
||||||
<string name="error_otp_secret_key">A chave secreta deve estar em formato Base32.</string>
|
<string name="error_otp_secret_key">A chave secreta deve estar em formato Base32.</string>
|
||||||
<string name="error_copy_group_here">Mão pode copiar um grupo aqui.</string>
|
<string name="error_copy_group_here">Não pode copiar um grupo aqui.</string>
|
||||||
<string name="error_disallow_no_credentials">Ao menos uma credencial deve ser definida.</string>
|
<string name="error_disallow_no_credentials">Ao menos uma credencial deve ser definida.</string>
|
||||||
<string name="error_invalid_OTP">Segredo OTP inválido.</string>
|
<string name="error_invalid_OTP">Segredo OTP inválido.</string>
|
||||||
<string name="entry_otp">OTP</string>
|
<string name="entry_otp">OTP</string>
|
||||||
@@ -400,7 +400,7 @@
|
|||||||
<string name="build_label">Compilação %1$s</string>
|
<string name="build_label">Compilação %1$s</string>
|
||||||
<string name="retrieving_db_key">A criar a chave da base de dados…</string>
|
<string name="retrieving_db_key">A criar a chave da base de dados…</string>
|
||||||
<string name="clipboard">Área de transferência</string>
|
<string name="clipboard">Área de transferência</string>
|
||||||
<string name="list_entries_show_username_summary">Mostrar nomes de utilizador em listas de entrada</string>
|
<string name="list_entries_show_username_summary">Mostrar nomes de utilizador na lista entradas</string>
|
||||||
<string name="list_entries_show_username_title">Mostrar nomes de utilizador</string>
|
<string name="list_entries_show_username_title">Mostrar nomes de utilizador</string>
|
||||||
<string name="error_load_database_KDF_memory">Não foi possível carregar a chave. Tente descarregar o \"Uso de Memória\" do KDF.</string>
|
<string name="error_load_database_KDF_memory">Não foi possível carregar a chave. Tente descarregar o \"Uso de Memória\" do KDF.</string>
|
||||||
<string name="error_load_database">Não foi possível abrir a sua base de dados.</string>
|
<string name="error_load_database">Não foi possível abrir a sua base de dados.</string>
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
<string name="clipboard_error">Alguns aparelhos não deixam as apps usarem a área de transferência.</string>
|
<string name="clipboard_error">Alguns aparelhos não deixam as apps usarem a área de transferência.</string>
|
||||||
<string name="clipboard_error_title">Erro na área de transferência</string>
|
<string name="clipboard_error_title">Erro na área de transferência</string>
|
||||||
<string name="allow">Permitir</string>
|
<string name="allow">Permitir</string>
|
||||||
<string name="extended_ASCII">ASCII Estendido</string>
|
<string name="extended_ASCII">ASCII Extendido</string>
|
||||||
<string name="brackets">Parênteses</string>
|
<string name="brackets">Parênteses</string>
|
||||||
<string name="application">App</string>
|
<string name="application">App</string>
|
||||||
<string name="app_timeout_summary">Inatividade antes de bloquear a base de dados</string>
|
<string name="app_timeout_summary">Inatividade antes de bloquear a base de dados</string>
|
||||||
@@ -452,4 +452,30 @@
|
|||||||
<string name="add_group">Adicionar grupo</string>
|
<string name="add_group">Adicionar grupo</string>
|
||||||
<string name="add_entry">Adicionar entrada</string>
|
<string name="add_entry">Adicionar entrada</string>
|
||||||
<string name="accept">Aceitar</string>
|
<string name="accept">Aceitar</string>
|
||||||
|
<string name="device_credential">Credencial do dispositivo</string>
|
||||||
|
<string name="advanced_unlock_prompt_not_initialized">Incapaz de inicializar o desbloqueio antecipado.</string>
|
||||||
|
<string name="advanced_unlock_scanning_error">Erro de desbloqueio avançado: %1$s</string>
|
||||||
|
<string name="advanced_unlock_not_recognized">Não conseguia reconhecer impressão de desbloqueio avançado</string>
|
||||||
|
<string name="advanced_unlock_invalid_key">Não consegue ler a chave de desbloqueio avançada. Por favor, apague-a e repita o procedimento de desbloqueio de reconhecimento.</string>
|
||||||
|
<string name="advanced_unlock_prompt_extract_credential_message">Extrair credencial de base de dados com dados de desbloqueio avançados</string>
|
||||||
|
<string name="advanced_unlock_prompt_extract_credential_title">Base de dados aberta com reconhecimento avançado de desbloqueio</string>
|
||||||
|
<string name="advanced_unlock_prompt_store_credential_message">Advertência: Ainda precisa de se lembrar da sua palavra-passe principal se usar o reconhecimento avançado de desbloqueio.</string>
|
||||||
|
<string name="advanced_unlock_prompt_store_credential_title">Reconhecimento avançado de desbloqueio</string>
|
||||||
|
<string name="open_advanced_unlock_prompt_store_credential">Abrir o alerta de desbloqueio avançado para armazenar as credenciais</string>
|
||||||
|
<string name="open_advanced_unlock_prompt_unlock_database">Abrir o alerta de desbloqueio avançado para desbloquear a base de dados</string>
|
||||||
|
<string name="biometric_security_update_required">É necessária uma actualização de segurança biométrica.</string>
|
||||||
|
<string name="configure_biometric">O escaneamento biométrico é suportado, mas não configurado.</string>
|
||||||
|
<string name="warning_database_revoked">Acesso ao ficheiro revogado pelo gestor do ficheiro, fechar a base de dados e reabri-la a partir da sua localização.</string>
|
||||||
|
<string name="warning_database_info_changed_options">Sobregravar as modificações externas, guardando a base de dados ou recarregando-a com as últimas alterações.</string>
|
||||||
|
<string name="warning_database_info_changed">A informação contida no seu ficheiro de base de dados foi modificada fora da aplicação.</string>
|
||||||
|
<string name="warning_empty_recycle_bin">Apagar permanentemente todos os nós do caixote do lixo da reciclagem\?</string>
|
||||||
|
<string name="registration_mode">Modo de registo</string>
|
||||||
|
<string name="save_mode">Modo Guardar</string>
|
||||||
|
<string name="search_mode">Modo de pesquisa</string>
|
||||||
|
<string name="menu_keystore_remove_key">Apagar chave de desbloqueio avançada</string>
|
||||||
|
<string name="menu_reload_database">Recarregar base de dados</string>
|
||||||
|
<string name="error_rebuild_list">Incapaz de reconstruir adequadamente a lista.</string>
|
||||||
|
<string name="error_database_uri_null">O URI da base de dados não pode ser recuperado.</string>
|
||||||
|
<string name="error_field_name_already_exists">O nome do campo já existe.</string>
|
||||||
|
<string name="error_registration_read_only">Salvar um novo item não é permitido numa base de dados só de leitura</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
* Manage new database format 4.1 #956
|
||||||
* Fix show button consistency #980
|
* Fix show button consistency #980
|
||||||
* Fix persistent notification #979
|
* Fix persistent notification #979
|
||||||
1
fastlane/metadata/android/en-US/changelogs/77.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/77.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
* Gestion du nouveau format de base de données 4.1 #956
|
||||||
* Correction de la consistance du bouton de visibilité #980
|
* Correction de la consistance du bouton de visibilité #980
|
||||||
* Correction de la notification persistante #979
|
* Correction de la notification persistante #979
|
||||||
1
fastlane/metadata/android/fr-FR/changelogs/77.txt
Normal file
1
fastlane/metadata/android/fr-FR/changelogs/77.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*
|
||||||
Reference in New Issue
Block a user