Add database reloading #794

This commit is contained in:
J-Jamet
2021-01-07 16:25:05 +01:00
parent 455fd0cd6d
commit 27eb095720
21 changed files with 305 additions and 80 deletions

View File

@@ -53,6 +53,7 @@ import com.kunzisoft.keepass.model.StreamDirection
import com.kunzisoft.keepass.notifications.AttachmentFileNotificationService import com.kunzisoft.keepass.notifications.AttachmentFileNotificationService
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_ENTRY_HISTORY import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_ENTRY_HISTORY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RELOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RESTORE_ENTRY_HISTORY import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RESTORE_ENTRY_HISTORY
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
@@ -151,6 +152,10 @@ class EntryActivity : LockingActivity() {
if (result.isSuccess) if (result.isSuccess)
finish() finish()
} }
ACTION_DATABASE_RELOAD_TASK -> {
// Close the current activity
finish()
}
} }
coordinatorLayout?.showActionError(result) coordinatorLayout?.showActionError(result)
} }
@@ -501,6 +506,9 @@ class EntryActivity : LockingActivity() {
R.id.menu_save_database -> { R.id.menu_save_database -> {
mProgressDatabaseTaskProvider?.startDatabaseSave(!mReadOnly) mProgressDatabaseTaskProvider?.startDatabaseSave(!mReadOnly)
} }
R.id.menu_reload_database -> {
mProgressDatabaseTaskProvider?.startDatabaseReload(false)
}
android.R.id.home -> finish() // close this activity and return to preview activity (if there is any) android.R.id.home -> finish() // close this activity and return to preview activity (if there is any)
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)

View File

@@ -61,6 +61,7 @@ import com.kunzisoft.keepass.notifications.AttachmentFileNotificationService
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RELOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.otp.OtpElement
@@ -335,6 +336,10 @@ class EntryEditActivity : LockingActivity(),
Log.e(TAG, "Unable to retrieve entry after database action", e) Log.e(TAG, "Unable to retrieve entry after database action", e)
} }
} }
ACTION_DATABASE_RELOAD_TASK -> {
// Close the current activity
finish()
}
} }
coordinatorLayout?.showActionError(result) coordinatorLayout?.showActionError(result)
} }
@@ -610,13 +615,7 @@ class EntryEditActivity : LockingActivity(),
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
MenuUtil.contributionMenuInflater(menuInflater, menu)
val inflater = menuInflater
inflater.inflate(R.menu.database, menu)
// Save database not needed here
menu.findItem(R.id.menu_save_database)?.isVisible = false
MenuUtil.contributionMenuInflater(inflater, menu)
return true return true
} }
@@ -673,9 +672,6 @@ class EntryEditActivity : LockingActivity(),
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_save_database -> {
mProgressDatabaseTaskProvider?.startDatabaseSave(!mReadOnly)
}
R.id.menu_contribute -> { R.id.menu_contribute -> {
MenuUtil.onContributionItemSelected(this) MenuUtil.onContributionItemSelected(this)
return true return true

View File

@@ -70,6 +70,7 @@ import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Compa
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_GROUP_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RELOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY
@@ -342,6 +343,11 @@ class GroupActivity : LockingActivity(),
} }
} }
} }
ACTION_DATABASE_RELOAD_TASK -> {
// Reload the current activity
startActivity(intent)
finish()
}
} }
coordinatorLayout?.showActionError(result) coordinatorLayout?.showActionError(result)
@@ -1003,6 +1009,10 @@ class GroupActivity : LockingActivity(),
mProgressDatabaseTaskProvider?.startDatabaseSave(!mReadOnly) mProgressDatabaseTaskProvider?.startDatabaseSave(!mReadOnly)
return true return true
} }
R.id.menu_reload_database -> {
mProgressDatabaseTaskProvider?.startDatabaseReload(false)
return true
}
R.id.menu_empty_recycle_bin -> { R.id.menu_empty_recycle_bin -> {
mCurrentGroup?.getChildren()?.let { listChildren -> mCurrentGroup?.getChildren()?.let { listChildren ->
// Automatically delete all elements // Automatically delete all elements

View File

@@ -720,7 +720,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
when (resultCode) { when (resultCode) {
LockingActivity.RESULT_EXIT_LOCK -> { LockingActivity.RESULT_EXIT_LOCK -> {
clearCredentialsViews() clearCredentialsViews()
Database.getInstance().closeAndClear(UriUtil.getBinaryDir(this)) Database.getInstance().clearAndClose(UriUtil.getBinaryDir(this))
} }
Activity.RESULT_CANCELED -> { Activity.RESULT_CANCELED -> {
clearCredentialsViews() clearCredentialsViews()

View File

@@ -34,7 +34,7 @@ class App : MultiDexApplication() {
} }
override fun onTerminate() { override fun onTerminate() {
Database.getInstance().closeAndClear(UriUtil.getBinaryDir(this)) Database.getInstance().clearAndClose(UriUtil.getBinaryDir(this))
super.onTerminate() super.onTerminate()
} }
} }

View File

@@ -26,7 +26,6 @@ import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.closeDatabase
class CreateDatabaseRunnable(context: Context, class CreateDatabaseRunnable(context: Context,
private val mDatabase: Database, private val mDatabase: Database,
@@ -47,7 +46,7 @@ class CreateDatabaseRunnable(context: Context,
createData(mDatabaseUri, databaseName, rootName) createData(mDatabaseUri, databaseName, rootName)
} }
} catch (e: Exception) { } catch (e: Exception) {
mDatabase.closeAndClear(UriUtil.getBinaryDir(context)) mDatabase.clearAndClose(UriUtil.getBinaryDir(context))
setError(e) setError(e)
} }

View File

@@ -31,7 +31,6 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.closeDatabase
class LoadDatabaseRunnable(private val context: Context, class LoadDatabaseRunnable(private val context: Context,
private val mDatabase: Database, private val mDatabase: Database,
@@ -47,7 +46,7 @@ class LoadDatabaseRunnable(private val context: Context,
override fun onStartRun() { override fun onStartRun() {
// Clear before we load // Clear before we load
mDatabase.closeAndClear(UriUtil.getBinaryDir(context)) mDatabase.clearAndClose(UriUtil.getBinaryDir(context))
} }
override fun onActionRun() { override fun onActionRun() {
@@ -83,7 +82,7 @@ class LoadDatabaseRunnable(private val context: Context,
// Register the current time to init the lock timer // Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context) PreferencesUtil.saveCurrentTime(context)
} else { } else {
mDatabase.closeAndClear(UriUtil.getBinaryDir(context)) mDatabase.clearAndClose(UriUtil.getBinaryDir(context))
} }
} }

View File

@@ -47,6 +47,7 @@ import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Compa
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_ENTRY_HISTORY import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_ENTRY_HISTORY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RELOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RESTORE_ENTRY_HISTORY import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RESTORE_ENTRY_HISTORY
@@ -294,6 +295,13 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
, ACTION_DATABASE_LOAD_TASK) , ACTION_DATABASE_LOAD_TASK)
} }
fun startDatabaseReload(fixDuplicateUuid: Boolean) {
start(Bundle().apply {
putBoolean(DatabaseTaskNotificationService.FIX_DUPLICATE_UUID_KEY, fixDuplicateUuid)
}
, ACTION_DATABASE_RELOAD_TASK)
}
fun startDatabaseAssignPassword(databaseUri: Uri, fun startDatabaseAssignPassword(databaseUri: Uri,
masterPasswordChecked: Boolean, masterPasswordChecked: Boolean,
masterPassword: String?, masterPassword: String?,

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.database.action
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtil
class ReloadDatabaseRunnable(private val context: Context,
private val mDatabase: Database,
private val mFixDuplicateUUID: Boolean,
private val progressTaskUpdater: ProgressTaskUpdater?,
private val mLoadDatabaseResult: ((Result) -> Unit)?)
: ActionRunnable() {
override fun onStartRun() {
// Clear before we load
mDatabase.clear(UriUtil.getBinaryDir(context))
}
override fun onActionRun() {
try {
mDatabase.reloadData(context.contentResolver,
UriUtil.getBinaryDir(context),
mFixDuplicateUUID,
progressTaskUpdater)
}
catch (e: DuplicateUuidDatabaseException) {
setError(e)
}
catch (e: LoadDatabaseException) {
setError(e)
}
if (result.isSuccess) {
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
} else {
mDatabase.clearAndClose(UriUtil.getBinaryDir(context))
}
}
override fun onFinishRun() {
mLoadDatabaseResult?.invoke(result)
}
}

View File

@@ -31,10 +31,7 @@ import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.NodeIdInt import com.kunzisoft.keepass.database.element.node.NodeIdInt
import com.kunzisoft.keepass.database.element.node.NodeIdUUID import com.kunzisoft.keepass.database.element.node.NodeIdUUID
import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm
import com.kunzisoft.keepass.database.exception.DatabaseOutputException import com.kunzisoft.keepass.database.exception.*
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.database.exception.SignatureDatabaseException
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.input.DatabaseInputKDB import com.kunzisoft.keepass.database.file.input.DatabaseInputKDB
@@ -330,29 +327,11 @@ class Database {
} }
@Throws(LoadDatabaseException::class) @Throws(LoadDatabaseException::class)
fun loadData(uri: Uri, password: String?, keyfile: Uri?, private fun readDatabaseStream(contentResolver: ContentResolver, uri: Uri,
readOnly: Boolean, openDatabaseKDB: (InputStream) -> DatabaseKDB,
contentResolver: ContentResolver, openDatabaseKDBX: (InputStream) -> DatabaseKDBX) {
cacheDirectory: File,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?) {
this.fileUri = uri
isReadOnly = readOnly
if (uri.scheme == "file") {
val file = File(uri.path!!)
isReadOnly = !file.canWrite()
}
// Pass KeyFile Uri as InputStreams
var databaseInputStream: InputStream? = null var databaseInputStream: InputStream? = null
var keyFileInputStream: InputStream? = null
try { try {
// Get keyFile inputStream
keyfile?.let {
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyfile)
}
// Load Data, pass Uris as InputStreams // Load Data, pass Uris as InputStreams
val databaseStream = UriUtil.getUriInputStream(contentResolver, uri) val databaseStream = UriUtil.getUriInputStream(contentResolver, uri)
?: throw IOException("Database input stream cannot be retrieve") ?: throw IOException("Database input stream cannot be retrieve")
@@ -374,22 +353,10 @@ class Database {
when { when {
// Header of database KDB // Header of database KDB
DatabaseHeaderKDB.matchesHeader(sig1, sig2) -> setDatabaseKDB(DatabaseInputKDB( DatabaseHeaderKDB.matchesHeader(sig1, sig2) -> setDatabaseKDB(openDatabaseKDB(databaseInputStream))
cacheDirectory,
fixDuplicateUUID)
.openDatabase(databaseInputStream,
password,
keyFileInputStream,
progressTaskUpdater))
// Header of database KDBX // Header of database KDBX
DatabaseHeaderKDBX.matchesHeader(sig1, sig2) -> setDatabaseKDBX(DatabaseInputKDBX( DatabaseHeaderKDBX.matchesHeader(sig1, sig2) -> setDatabaseKDBX(openDatabaseKDBX(databaseInputStream))
cacheDirectory,
fixDuplicateUUID)
.openDatabase(databaseInputStream,
password,
keyFileInputStream,
progressTaskUpdater))
// Header not recognized // Header not recognized
else -> throw SignatureDatabaseException() else -> throw SignatureDatabaseException()
@@ -397,17 +364,92 @@ class Database {
this.mSearchHelper = SearchHelper() this.mSearchHelper = SearchHelper()
loaded = true loaded = true
} catch (e: LoadDatabaseException) { } catch (e: LoadDatabaseException) {
throw e throw e
} finally {
databaseInputStream?.close()
}
}
@Throws(LoadDatabaseException::class)
fun loadData(uri: Uri, password: String?, keyfile: Uri?,
readOnly: Boolean,
contentResolver: ContentResolver,
cacheDirectory: File,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?) {
// Save database URI
this.fileUri = uri
// Check if the file is writable
this.isReadOnly = readOnly
if (uri.scheme == "file") {
val file = File(uri.path!!)
isReadOnly = !file.canWrite()
}
// Pass KeyFile Uri as InputStreams
var keyFileInputStream: InputStream? = null
try {
// Get keyFile inputStream
keyfile?.let {
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyfile)
}
// Read database stream for the first time
readDatabaseStream(contentResolver, uri,
{ databaseInputStream ->
DatabaseInputKDB(cacheDirectory)
.openDatabase(databaseInputStream,
password,
keyFileInputStream,
fixDuplicateUUID,
progressTaskUpdater)
},
{ databaseInputStream ->
DatabaseInputKDBX(cacheDirectory)
.openDatabase(databaseInputStream,
password,
keyFileInputStream,
fixDuplicateUUID,
progressTaskUpdater)
}
)
} catch (e: Exception) { } catch (e: Exception) {
throw FileNotFoundDatabaseException() throw FileNotFoundDatabaseException()
} finally { } finally {
keyFileInputStream?.close() keyFileInputStream?.close()
databaseInputStream?.close()
} }
} }
@Throws(LoadDatabaseException::class)
fun reloadData(contentResolver: ContentResolver,
cacheDirectory: File,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?) {
// Retrieve the stream from the old database URI
fileUri?.let { oldDatabaseUri ->
readDatabaseStream(contentResolver, oldDatabaseUri,
{ databaseInputStream ->
DatabaseInputKDB(cacheDirectory)
.openDatabase(databaseInputStream,
masterKey,
fixDuplicateUUID,
progressTaskUpdater)
},
{ databaseInputStream ->
DatabaseInputKDBX(cacheDirectory)
.openDatabase(databaseInputStream,
masterKey,
fixDuplicateUUID,
progressTaskUpdater)
}
)
} ?: throw IODatabaseException()
}
fun isGroupSearchable(group: Group, omitBackup: Boolean): Boolean { fun isGroupSearchable(group: Group, omitBackup: Boolean): Boolean {
return mDatabaseKDB?.isGroupSearchable(group.groupKDB, omitBackup) ?: return mDatabaseKDB?.isGroupSearchable(group.groupKDB, omitBackup) ?:
mDatabaseKDBX?.isGroupSearchable(group.groupKDBX, omitBackup) ?: mDatabaseKDBX?.isGroupSearchable(group.groupKDBX, omitBackup) ?:
@@ -531,7 +573,7 @@ class Database {
this.fileUri = uri this.fileUri = uri
} }
fun closeAndClear(filesDirectory: File? = null) { fun clear(filesDirectory: File? = null) {
drawFactory.clearCache() drawFactory.clearCache()
// Delete the cache of the database if present // Delete the cache of the database if present
mDatabaseKDB?.clearCache() mDatabaseKDB?.clearCache()
@@ -544,7 +586,10 @@ class Database {
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Unable to clear the directory cache.", e) Log.e(TAG, "Unable to clear the directory cache.", e)
} }
}
fun clearAndClose(filesDirectory: File? = null) {
clear(filesDirectory)
this.mDatabaseKDB = null this.mDatabaseKDB = null
this.mDatabaseKDBX = null this.mDatabaseKDBX = null
this.fileUri = null this.fileUri = null

View File

@@ -41,6 +41,13 @@ abstract class DatabaseInput<PwDb : DatabaseVersioned<*, *, *, *>>
abstract fun openDatabase(databaseInputStream: InputStream, abstract fun openDatabase(databaseInputStream: InputStream,
password: String?, password: String?,
keyInputStream: InputStream?, keyInputStream: InputStream?,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?): PwDb progressTaskUpdater: ProgressTaskUpdater?): PwDb
@Throws(LoadDatabaseException::class)
abstract fun openDatabase(databaseInputStream: InputStream,
masterKey: ByteArray,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?): PwDb
} }

View File

@@ -45,8 +45,7 @@ import javax.crypto.spec.SecretKeySpec
/** /**
* Load a KDB database file. * Load a KDB database file.
*/ */
class DatabaseInputKDB(cacheDirectory: File, class DatabaseInputKDB(cacheDirectory: File)
private val fixDuplicateUUID: Boolean = false)
: DatabaseInput<DatabaseKDB>(cacheDirectory) { : DatabaseInput<DatabaseKDB>(cacheDirectory) {
private lateinit var mDatabaseToOpen: DatabaseKDB private lateinit var mDatabaseToOpen: DatabaseKDB
@@ -55,7 +54,28 @@ class DatabaseInputKDB(cacheDirectory: File,
override fun openDatabase(databaseInputStream: InputStream, override fun openDatabase(databaseInputStream: InputStream,
password: String?, password: String?,
keyInputStream: InputStream?, keyInputStream: InputStream?,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDB { progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDB {
return openDatabase(databaseInputStream, fixDuplicateUUID, progressTaskUpdater) {
mDatabaseToOpen.retrieveMasterKey(password, keyInputStream)
}
}
@Throws(LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
masterKey: ByteArray,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDB {
return openDatabase(databaseInputStream, fixDuplicateUUID, progressTaskUpdater) {
mDatabaseToOpen.masterKey = masterKey
}
}
@Throws(LoadDatabaseException::class)
private fun openDatabase(databaseInputStream: InputStream,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?,
assignMasterKey: (() -> Unit)? = null): DatabaseKDB {
try { try {
// Load entire file, most of it's encrypted. // Load entire file, most of it's encrypted.
@@ -84,7 +104,7 @@ class DatabaseInputKDB(cacheDirectory: File,
mDatabaseToOpen = DatabaseKDB() mDatabaseToOpen = DatabaseKDB()
mDatabaseToOpen.changeDuplicateId = fixDuplicateUUID mDatabaseToOpen.changeDuplicateId = fixDuplicateUUID
mDatabaseToOpen.retrieveMasterKey(password, keyInputStream) assignMasterKey?.invoke()
// Select algorithm // Select algorithm
when { when {

View File

@@ -63,8 +63,7 @@ import javax.crypto.Cipher
import javax.crypto.CipherInputStream import javax.crypto.CipherInputStream
import kotlin.math.min import kotlin.math.min
class DatabaseInputKDBX(cacheDirectory: File, class DatabaseInputKDBX(cacheDirectory: File)
private val fixDuplicateUUID: Boolean = false)
: DatabaseInput<DatabaseKDBX>(cacheDirectory) { : DatabaseInput<DatabaseKDBX>(cacheDirectory) {
private var randomStream: StreamCipher? = null private var randomStream: StreamCipher? = null
@@ -98,12 +97,30 @@ class DatabaseInputKDBX(cacheDirectory: File,
override fun openDatabase(databaseInputStream: InputStream, override fun openDatabase(databaseInputStream: InputStream,
password: String?, password: String?,
keyInputStream: InputStream?, keyInputStream: InputStream?,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDBX { progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDBX {
return openDatabase(databaseInputStream, fixDuplicateUUID, progressTaskUpdater) { header ->
mDatabase.retrieveMasterKey(password, keyInputStream)
}
}
@Throws(LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
masterKey: ByteArray,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDBX {
return openDatabase(databaseInputStream, fixDuplicateUUID, progressTaskUpdater) {
mDatabase.masterKey = masterKey
}
}
@Throws(LoadDatabaseException::class)
private fun openDatabase(databaseInputStream: InputStream,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?,
assignMasterKey: ((header: DatabaseHeaderKDBX) -> Unit)? = null): DatabaseKDBX {
try { try {
// TODO performance
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key) progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
mDatabase = DatabaseKDBX() mDatabase = DatabaseKDBX()
mDatabase.changeDuplicateId = fixDuplicateUUID mDatabase.changeDuplicateId = fixDuplicateUUID
@@ -116,9 +133,8 @@ class DatabaseInputKDBX(cacheDirectory: File,
hashOfHeader = headerAndHash.hash hashOfHeader = headerAndHash.hash
val pbHeader = headerAndHash.header val pbHeader = headerAndHash.header
mDatabase.retrieveMasterKey(password, keyInputStream) assignMasterKey?.invoke(header)
mDatabase.makeFinalKey(header.masterSeed) mDatabase.makeFinalKey(header.masterSeed)
// TODO performance
progressTaskUpdater?.updateMessage(R.string.decrypting_db) progressTaskUpdater?.updateMessage(R.string.decrypting_db)
val engine: CipherEngine val engine: CipherEngine

View File

@@ -180,6 +180,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
val actionRunnable: ActionRunnable? = when (intentAction) { val actionRunnable: ActionRunnable? = when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> buildDatabaseCreateActionTask(intent) ACTION_DATABASE_CREATE_TASK -> buildDatabaseCreateActionTask(intent)
ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent) ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent)
ACTION_DATABASE_RELOAD_TASK -> buildDatabaseReloadActionTask(intent)
ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent) ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent)
ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent) ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent)
ACTION_DATABASE_UPDATE_GROUP_TASK -> buildDatabaseUpdateGroupActionTask(intent) ACTION_DATABASE_UPDATE_GROUP_TASK -> buildDatabaseUpdateGroupActionTask(intent)
@@ -258,7 +259,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
return when (intentAction) { return when (intentAction) {
ACTION_DATABASE_LOAD_TASK, null -> { ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_RELOAD_TASK,
null -> {
START_STICKY START_STICKY
} }
else -> { else -> {
@@ -292,7 +295,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
else -> { else -> {
when (intentAction) { when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> R.string.creating_database ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
ACTION_DATABASE_LOAD_TASK -> R.string.loading_database ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_RELOAD_TASK -> R.string.loading_database
ACTION_DATABASE_SAVE -> R.string.saving_database ACTION_DATABASE_SAVE -> R.string.saving_database
else -> { else -> {
R.string.command_execution R.string.command_execution
@@ -302,13 +306,15 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
mMessageId = when (intentAction) { mMessageId = when (intentAction) {
ACTION_DATABASE_LOAD_TASK -> null ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_RELOAD_TASK -> null
else -> null else -> null
} }
mWarningId = mWarningId =
if (!saveAction if (!saveAction
|| intentAction == ACTION_DATABASE_LOAD_TASK) || intentAction == ACTION_DATABASE_LOAD_TASK
|| intentAction == ACTION_DATABASE_RELOAD_TASK)
null null
else else
R.string.do_not_kill_app R.string.do_not_kill_app
@@ -509,6 +515,23 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
} }
private fun buildDatabaseReloadActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(FIX_DUPLICATE_UUID_KEY)) {
ReloadDatabaseRunnable(
this,
mDatabase,
intent.getBooleanExtra(FIX_DUPLICATE_UUID_KEY, false),
this
) { result ->
// No need to add each info to reload database
result.data = Bundle()
}
} else {
null
}
}
private fun buildDatabaseAssignPasswordActionTask(intent: Intent): ActionRunnable? { private fun buildDatabaseAssignPasswordActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(DATABASE_URI_KEY) return if (intent.hasExtra(DATABASE_URI_KEY)
&& intent.hasExtra(MASTER_PASSWORD_CHECKED_KEY) && intent.hasExtra(MASTER_PASSWORD_CHECKED_KEY)
@@ -814,6 +837,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
const val ACTION_DATABASE_CREATE_TASK = "ACTION_DATABASE_CREATE_TASK" const val ACTION_DATABASE_CREATE_TASK = "ACTION_DATABASE_CREATE_TASK"
const val ACTION_DATABASE_LOAD_TASK = "ACTION_DATABASE_LOAD_TASK" const val ACTION_DATABASE_LOAD_TASK = "ACTION_DATABASE_LOAD_TASK"
const val ACTION_DATABASE_RELOAD_TASK = "ACTION_DATABASE_RELOAD_TASK"
const val ACTION_DATABASE_ASSIGN_PASSWORD_TASK = "ACTION_DATABASE_ASSIGN_PASSWORD_TASK" const val ACTION_DATABASE_ASSIGN_PASSWORD_TASK = "ACTION_DATABASE_ASSIGN_PASSWORD_TASK"
const val ACTION_DATABASE_CREATE_GROUP_TASK = "ACTION_DATABASE_CREATE_GROUP_TASK" const val ACTION_DATABASE_CREATE_GROUP_TASK = "ACTION_DATABASE_CREATE_GROUP_TASK"
const val ACTION_DATABASE_UPDATE_GROUP_TASK = "ACTION_DATABASE_UPDATE_GROUP_TASK" const val ACTION_DATABASE_UPDATE_GROUP_TASK = "ACTION_DATABASE_UPDATE_GROUP_TASK"

View File

@@ -552,6 +552,10 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
settingActivity?.mProgressDatabaseTaskProvider?.startDatabaseSave(!mDatabaseReadOnly) settingActivity?.mProgressDatabaseTaskProvider?.startDatabaseSave(!mDatabaseReadOnly)
true true
} }
R.id.menu_reload_database -> {
settingActivity?.mProgressDatabaseTaskProvider?.startDatabaseReload(false)
return true
}
else -> { else -> {
// Check the time lock before launching settings // Check the time lock before launching settings

View File

@@ -37,6 +37,7 @@ import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.view.showActionError import com.kunzisoft.keepass.view.showActionError
@@ -95,12 +96,19 @@ open class SettingsActivity
backupManager = BackupManager(this) backupManager = BackupManager(this)
mProgressDatabaseTaskProvider?.onActionFinish = { actionTask, result -> mProgressDatabaseTaskProvider?.onActionFinish = { actionTask, result ->
// Call result in fragment when (actionTask) {
(supportFragmentManager DatabaseTaskNotificationService.ACTION_DATABASE_RELOAD_TASK -> {
.findFragmentByTag(TAG_NESTED) as NestedSettingsFragment?) // Close the current activity
?.onProgressDialogThreadResult(actionTask, result) finish()
}
coordinatorLayout?.showActionError(result) else -> {
// Call result in fragment
(supportFragmentManager
.findFragmentByTag(TAG_NESTED) as NestedSettingsFragment?)
?.onProgressDialogThreadResult(actionTask, result)
coordinatorLayout?.showActionError(result)
}
}
} }
} }

View File

@@ -138,5 +138,5 @@ fun Context.closeDatabase() {
cancelAll() cancelAll()
} }
// Clear data // Clear data
Database.getInstance().closeAndClear(UriUtil.getBinaryDir(this)) Database.getInstance().clearAndClose(UriUtil.getBinaryDir(this))
} }

View File

@@ -0,0 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportHeight="24"
android:viewportWidth="24" >
<path android:fillColor="#FFFFFF" android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
</vector>

View File

@@ -22,6 +22,6 @@
<item android:id="@+id/menu_contribute" <item android:id="@+id/menu_contribute"
android:icon="@drawable/ic_heart_white_24dp" android:icon="@drawable/ic_heart_white_24dp"
android:title="@string/contribute" android:title="@string/contribute"
android:orderInCategory="95" android:orderInCategory="99"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
</menu> </menu>

View File

@@ -24,4 +24,9 @@
android:title="@string/menu_save_database" android:title="@string/menu_save_database"
android:orderInCategory="95" android:orderInCategory="95"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item android:id="@+id/menu_reload_database"
android:icon="@drawable/ic_reload_white_24dp"
android:title="@string/menu_reload_database"
android:orderInCategory="96"
app:showAsAction="ifRoom" />
</menu> </menu>

View File

@@ -185,6 +185,7 @@
<string name="menu_hide_password">Hide password</string> <string name="menu_hide_password">Hide password</string>
<string name="menu_lock">Lock database</string> <string name="menu_lock">Lock database</string>
<string name="menu_save_database">Save database</string> <string name="menu_save_database">Save database</string>
<string name="menu_reload_database">Reload database</string>
<string name="menu_open">Open</string> <string name="menu_open">Open</string>
<string name="menu_search">Search</string> <string name="menu_search">Search</string>
<string name="menu_showpass">Show password</string> <string name="menu_showpass">Show password</string>