mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Refactor load database
This commit is contained in:
@@ -311,7 +311,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
||||
private val keyFileUri: Uri?) : ActionRunnable() {
|
||||
|
||||
override fun run() {
|
||||
finishRun(true, null)
|
||||
finishRun(true)
|
||||
}
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.kunzisoft.keepass.database.action.ProgressDialogThread
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.education.PasswordActivityEducation
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
|
||||
import com.kunzisoft.keepass.database.search.SearchDbHelper
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.utils.MenuUtil
|
||||
@@ -69,7 +70,6 @@ import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
|
||||
import com.kunzisoft.keepass.view.asError
|
||||
import kotlinx.android.synthetic.main.activity_password.*
|
||||
import java.io.FileNotFoundException
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class PasswordActivity : StylishActivity() {
|
||||
|
||||
@@ -87,6 +87,8 @@ class PasswordActivity : StylishActivity() {
|
||||
private var enableButtonOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
|
||||
|
||||
private var mDatabaseFileUri: Uri? = null
|
||||
private var mDatabaseKeyFileUri: Uri? = null
|
||||
|
||||
private var prefs: SharedPreferences? = null
|
||||
|
||||
private var mRememberKeyFile: Boolean = false
|
||||
@@ -101,8 +103,7 @@ class PasswordActivity : StylishActivity() {
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
mRememberKeyFile = prefs!!.getBoolean(getString(R.string.keyfile_key),
|
||||
resources.getBoolean(R.bool.keyfile_default))
|
||||
mRememberKeyFile = PreferencesUtil.rememberKeyFiles(this)
|
||||
|
||||
setContentView(R.layout.activity_password)
|
||||
|
||||
@@ -222,6 +223,7 @@ class PasswordActivity : StylishActivity() {
|
||||
|
||||
private fun onPostInitUri(databaseFileUri: Uri?, keyFileUri: Uri?) {
|
||||
mDatabaseFileUri = databaseFileUri
|
||||
mDatabaseKeyFileUri = keyFileUri
|
||||
|
||||
// Define title
|
||||
databaseFileUri?.let {
|
||||
@@ -391,14 +393,18 @@ class PasswordActivity : StylishActivity() {
|
||||
keyFile: Uri?,
|
||||
cipherDatabaseEntity: CipherDatabaseEntity? = null) {
|
||||
val keyPassword = if (checkboxPasswordView?.isChecked != true) null else password
|
||||
val keyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
|
||||
loadDatabase(keyPassword, keyFileUri, cipherDatabaseEntity)
|
||||
verifyKeyFileCheckbox(keyFile)
|
||||
loadDatabase(keyPassword, mDatabaseKeyFileUri, cipherDatabaseEntity)
|
||||
}
|
||||
|
||||
private fun verifyKeyFileCheckboxAndLoadDatabase(password: String?) {
|
||||
val keyFile: Uri? = UriUtil.parse(keyFileView?.text?.toString())
|
||||
val keyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
|
||||
loadDatabase(password, keyFileUri)
|
||||
verifyKeyFileCheckbox(keyFile)
|
||||
loadDatabase(password, mDatabaseKeyFileUri)
|
||||
}
|
||||
|
||||
private fun verifyKeyFileCheckbox(keyFile: Uri?) {
|
||||
mDatabaseKeyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
|
||||
}
|
||||
|
||||
private fun removePassword() {
|
||||
@@ -423,11 +429,13 @@ class PasswordActivity : StylishActivity() {
|
||||
ProgressDialogThread(this,
|
||||
{ progressTaskUpdater ->
|
||||
LoadDatabaseRunnable(
|
||||
WeakReference(this@PasswordActivity),
|
||||
database,
|
||||
databaseUri,
|
||||
password,
|
||||
keyFile,
|
||||
this@PasswordActivity.contentResolver,
|
||||
this@PasswordActivity.filesDir,
|
||||
SearchDbHelper(PreferencesUtil.omitBackup(this@PasswordActivity)),
|
||||
progressTaskUpdater,
|
||||
AfterLoadingDatabase(database, password, cipherDatabaseEntity))
|
||||
},
|
||||
@@ -453,6 +461,15 @@ class PasswordActivity : StylishActivity() {
|
||||
}
|
||||
|
||||
if (result.isSuccess) {
|
||||
// Save keyFile in app database
|
||||
if (PreferencesUtil.rememberKeyFiles(this@PasswordActivity)) {
|
||||
mDatabaseFileUri?.let { databaseUri ->
|
||||
mDatabaseKeyFileUri?.let { keyFileUri ->
|
||||
saveKeyFileData(databaseUri, keyFileUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the password in view in all cases
|
||||
removePassword()
|
||||
|
||||
@@ -467,14 +484,29 @@ class PasswordActivity : StylishActivity() {
|
||||
}
|
||||
|
||||
} else {
|
||||
if (result.message != null && result.message!!.isNotEmpty()) {
|
||||
Snackbar.make(activity_password_coordinator_layout, result.message!!, Snackbar.LENGTH_LONG).asError().show()
|
||||
var errorMessage = result.message
|
||||
val resultException = result.exception
|
||||
|
||||
Log.e(TAG, errorMessage)
|
||||
|
||||
if (errorMessage != null && errorMessage.isNotEmpty()) {
|
||||
if (resultException != null)
|
||||
errorMessage = "${resultException.getLocalizedMessage(resources)} $errorMessage"
|
||||
Snackbar.make(activity_password_coordinator_layout, errorMessage, Snackbar.LENGTH_LONG).asError().show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveKeyFileData(databaseUri: Uri, keyUri: Uri?) {
|
||||
var keyFileUri = keyUri
|
||||
if (!mRememberKeyFile) {
|
||||
keyFileUri = null
|
||||
}
|
||||
FileDatabaseHistoryAction.getInstance(this).addOrUpdateDatabaseUri(databaseUri, keyFileUri)
|
||||
}
|
||||
|
||||
private fun checkAndLaunchGroupActivity(database: Database, password: String?) {
|
||||
if (database.validatePasswordEncoding(password)) {
|
||||
launchGroupActivity()
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.action
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import java.io.IOException
|
||||
@@ -63,7 +63,7 @@ open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
|
||||
// To save the database
|
||||
super.run()
|
||||
finishRun(true)
|
||||
} catch (e: InvalidKeyFileException) {
|
||||
} catch (e: LoadDatabaseInvalidKeyFileException) {
|
||||
erase(mBackupKey)
|
||||
finishRun(false, e.message)
|
||||
} catch (e: IOException) {
|
||||
|
||||
@@ -19,118 +19,43 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.action
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.preference.PreferenceManager
|
||||
import androidx.annotation.StringRes
|
||||
import android.util.Log
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.*
|
||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
||||
import com.kunzisoft.keepass.database.search.SearchDbHelper
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.lang.ref.WeakReference
|
||||
import java.io.File
|
||||
|
||||
class LoadDatabaseRunnable(private val mWeakContext: WeakReference<Context>,
|
||||
private val mDatabase: Database,
|
||||
class LoadDatabaseRunnable(private val mDatabase: Database,
|
||||
private val mUri: Uri,
|
||||
private val mPass: String?,
|
||||
private val mKey: Uri?,
|
||||
private val contentResolver: ContentResolver,
|
||||
private val cacheDirectory: File,
|
||||
private val mSearchHelper: SearchDbHelper,
|
||||
private val progressTaskUpdater: ProgressTaskUpdater?,
|
||||
nestedAction: ActionRunnable)
|
||||
: ActionRunnable(nestedAction, executeNestedActionIfResultFalse = true) {
|
||||
|
||||
private val mRememberKeyFile: Boolean
|
||||
get() {
|
||||
return mWeakContext.get()?.let {
|
||||
PreferenceManager.getDefaultSharedPreferences(it)
|
||||
.getBoolean(it.getString(R.string.keyfile_key),
|
||||
it.resources.getBoolean(R.bool.keyfile_default))
|
||||
} ?: true
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
try {
|
||||
mWeakContext.get()?.let {
|
||||
mDatabase.loadData(it, mUri, mPass, mKey, progressTaskUpdater)
|
||||
saveFileData(mUri, mKey)
|
||||
finishRun(true)
|
||||
} ?: finishRun(false, "Context null")
|
||||
} catch (e: ArcFourException) {
|
||||
catchError(e, R.string.error_arc4)
|
||||
return
|
||||
} catch (e: InvalidPasswordException) {
|
||||
catchError(e, R.string.invalid_password)
|
||||
return
|
||||
} catch (e: ContentFileNotFoundException) {
|
||||
catchError(e, R.string.file_not_found_content)
|
||||
return
|
||||
} catch (e: FileNotFoundException) {
|
||||
catchError(e, R.string.file_not_found)
|
||||
return
|
||||
} catch (e: IOException) {
|
||||
var messageId = R.string.error_load_database
|
||||
e.message?.let {
|
||||
if (it.contains("Hash failed with code"))
|
||||
messageId = R.string.error_load_database_KDF_memory
|
||||
}
|
||||
catchError(e, messageId, true)
|
||||
return
|
||||
} catch (e: KeyFileEmptyException) {
|
||||
catchError(e, R.string.keyfile_is_empty)
|
||||
return
|
||||
} catch (e: InvalidAlgorithmException) {
|
||||
catchError(e, R.string.invalid_algorithm)
|
||||
return
|
||||
} catch (e: InvalidKeyFileException) {
|
||||
catchError(e, R.string.keyfile_does_not_exist)
|
||||
return
|
||||
} catch (e: InvalidDBSignatureException) {
|
||||
catchError(e, R.string.invalid_db_sig)
|
||||
return
|
||||
} catch (e: InvalidDBVersionException) {
|
||||
catchError(e, R.string.unsupported_db_version)
|
||||
return
|
||||
} catch (e: InvalidDBException) {
|
||||
catchError(e, R.string.error_invalid_db)
|
||||
return
|
||||
} catch (e: OutOfMemoryError) {
|
||||
catchError(e, R.string.error_out_of_memory)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
catchError(e, R.string.error_load_database, true)
|
||||
return
|
||||
mDatabase.loadData(mUri, mPass, mKey,
|
||||
contentResolver,
|
||||
cacheDirectory,
|
||||
mSearchHelper,
|
||||
progressTaskUpdater)
|
||||
finishRun(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun catchError(e: Throwable, @StringRes messageId: Int, addThrowableMessage: Boolean = false) {
|
||||
var errorMessage = mWeakContext.get()?.getString(messageId)
|
||||
Log.e(TAG, errorMessage, e)
|
||||
if (addThrowableMessage)
|
||||
errorMessage = errorMessage + " " + e.localizedMessage
|
||||
finishRun(false, errorMessage)
|
||||
}
|
||||
|
||||
private fun saveFileData(uri: Uri, key: Uri?) {
|
||||
var keyFileUri = key
|
||||
if (!mRememberKeyFile) {
|
||||
keyFileUri = null
|
||||
}
|
||||
mWeakContext.get()?.let {
|
||||
FileDatabaseHistoryAction.getInstance(it).addOrUpdateDatabaseUri(uri, keyFileUri)
|
||||
catch (e: LoadDatabaseException) {
|
||||
finishRun(false, e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
if (!result.isSuccess) {
|
||||
mDatabase.closeAndClear(mWeakContext.get()?.filesDir)
|
||||
mDatabase.closeAndClear(cacheDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = LoadDatabaseRunnable::class.java.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.action
|
||||
|
||||
import android.content.Context
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import java.io.IOException
|
||||
|
||||
@@ -37,7 +37,7 @@ abstract class SaveDatabaseRunnable(protected var context: Context,
|
||||
database.saveData(context.contentResolver)
|
||||
} catch (e: IOException) {
|
||||
finishRun(false, e.message)
|
||||
} catch (e: PwDbOutputException) {
|
||||
} catch (e: DatabaseOutputException) {
|
||||
finishRun(false, e.message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
@@ -39,7 +38,6 @@ import com.kunzisoft.keepass.database.file.save.PwDbV3Output
|
||||
import com.kunzisoft.keepass.database.file.save.PwDbV4Output
|
||||
import com.kunzisoft.keepass.database.search.SearchDbHelper
|
||||
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.stream.LEDataInputStream
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import com.kunzisoft.keepass.utils.SingletonHolder
|
||||
@@ -56,7 +54,8 @@ class Database {
|
||||
private var pwDatabaseV4: PwDatabaseV4? = null
|
||||
|
||||
private var mUri: Uri? = null
|
||||
private var searchHelper: SearchDbHelper? = null
|
||||
private var mSearchHelper: SearchDbHelper? = null
|
||||
|
||||
var isReadOnly = false
|
||||
|
||||
val drawFactory = IconDrawableFactory()
|
||||
@@ -209,78 +208,92 @@ class Database {
|
||||
this.mUri = databaseUri
|
||||
}
|
||||
|
||||
@Throws(IOException::class, InvalidDBException::class)
|
||||
fun loadData(ctx: Context, uri: Uri, password: String?, keyfile: Uri?, progressTaskUpdater: ProgressTaskUpdater?) {
|
||||
@Throws(LoadDatabaseException::class)
|
||||
fun loadData(uri: Uri, password: String?, keyfile: Uri?,
|
||||
contentResolver: ContentResolver,
|
||||
cacheDirectory: File,
|
||||
searchHelper: SearchDbHelper,
|
||||
progressTaskUpdater: ProgressTaskUpdater?) {
|
||||
|
||||
mUri = uri
|
||||
isReadOnly = false
|
||||
if (uri.scheme == "file") {
|
||||
val file = File(uri.path!!)
|
||||
isReadOnly = !file.canWrite()
|
||||
}
|
||||
|
||||
// Pass Uris as InputStreams
|
||||
val inputStream: InputStream?
|
||||
try {
|
||||
inputStream = UriUtil.getUriInputStream(ctx.contentResolver, uri)
|
||||
} catch (e: Exception) {
|
||||
Log.e("KPD", "Database::loadData", e)
|
||||
throw ContentFileNotFoundException.getInstance(uri)
|
||||
}
|
||||
|
||||
// Pass KeyFile Uri as InputStreams
|
||||
var keyFileInputStream: InputStream? = null
|
||||
keyfile?.let {
|
||||
mUri = uri
|
||||
isReadOnly = false
|
||||
if (uri.scheme == "file") {
|
||||
val file = File(uri.path!!)
|
||||
isReadOnly = !file.canWrite()
|
||||
}
|
||||
|
||||
// Pass Uris as InputStreams
|
||||
val inputStream: InputStream?
|
||||
try {
|
||||
keyFileInputStream = UriUtil.getUriInputStream(ctx.contentResolver, keyfile)
|
||||
inputStream = UriUtil.getUriInputStream(contentResolver, uri)
|
||||
} catch (e: Exception) {
|
||||
Log.e("KPD", "Database::loadData", e)
|
||||
throw ContentFileNotFoundException.getInstance(keyfile)
|
||||
throw LoadDatabaseFileNotFoundException()
|
||||
}
|
||||
}
|
||||
|
||||
// Load Data
|
||||
// Pass KeyFile Uri as InputStreams
|
||||
var keyFileInputStream: InputStream? = null
|
||||
keyfile?.let {
|
||||
try {
|
||||
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyfile)
|
||||
} catch (e: Exception) {
|
||||
Log.e("KPD", "Database::loadData", e)
|
||||
throw LoadDatabaseFileNotFoundException()
|
||||
}
|
||||
}
|
||||
|
||||
val bufferedInputStream = BufferedInputStream(inputStream)
|
||||
if (!bufferedInputStream.markSupported()) {
|
||||
throw IOException("Input stream does not support mark.")
|
||||
}
|
||||
// Load Data
|
||||
|
||||
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
||||
bufferedInputStream.mark(10)
|
||||
val bufferedInputStream = BufferedInputStream(inputStream)
|
||||
if (!bufferedInputStream.markSupported()) {
|
||||
throw IOException("Input stream does not support mark.")
|
||||
}
|
||||
|
||||
// Get the file directory to save the attachments
|
||||
val sig1 = LEDataInputStream.readInt(bufferedInputStream)
|
||||
val sig2 = LEDataInputStream.readInt(bufferedInputStream)
|
||||
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
||||
bufferedInputStream.mark(10)
|
||||
|
||||
// Return to the start
|
||||
bufferedInputStream.reset()
|
||||
// Get the file directory to save the attachments
|
||||
val sig1 = LEDataInputStream.readInt(bufferedInputStream)
|
||||
val sig2 = LEDataInputStream.readInt(bufferedInputStream)
|
||||
|
||||
when {
|
||||
// Header of database V3
|
||||
PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3()
|
||||
.openDatabase(bufferedInputStream,
|
||||
password,
|
||||
keyFileInputStream,
|
||||
progressTaskUpdater))
|
||||
// Return to the start
|
||||
bufferedInputStream.reset()
|
||||
|
||||
// Header of database V4
|
||||
PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(ctx.filesDir)
|
||||
.openDatabase(bufferedInputStream,
|
||||
password,
|
||||
keyFileInputStream,
|
||||
progressTaskUpdater))
|
||||
when {
|
||||
// Header of database V3
|
||||
PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3()
|
||||
.openDatabase(bufferedInputStream,
|
||||
password,
|
||||
keyFileInputStream,
|
||||
progressTaskUpdater))
|
||||
|
||||
// Header not recognized
|
||||
else -> throw InvalidDBSignatureException()
|
||||
}
|
||||
// Header of database V4
|
||||
PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(cacheDirectory)
|
||||
.openDatabase(bufferedInputStream,
|
||||
password,
|
||||
keyFileInputStream,
|
||||
progressTaskUpdater))
|
||||
|
||||
try {
|
||||
searchHelper = SearchDbHelper(PreferencesUtil.omitBackup(ctx))
|
||||
// Header not recognized
|
||||
else -> throw LoadDatabaseSignatureException()
|
||||
}
|
||||
|
||||
this.mSearchHelper = searchHelper
|
||||
loaded = true
|
||||
|
||||
} catch (e: LoadDatabaseException) {
|
||||
throw e
|
||||
} catch (e: IOException) {
|
||||
if (e.message?.contains("Hash failed with code") == true)
|
||||
throw LoadDatabaseKDFMemoryException(e)
|
||||
else
|
||||
throw LoadDatabaseIOException(e)
|
||||
} catch (e: OutOfMemoryError) {
|
||||
throw LoadDatabaseNoMemoryException(e)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Load can't be performed with this Database version", e)
|
||||
loaded = false
|
||||
throw LoadDatabaseException(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +305,7 @@ class Database {
|
||||
|
||||
@JvmOverloads
|
||||
fun search(str: String, max: Int = Integer.MAX_VALUE): GroupVersioned? {
|
||||
return searchHelper?.search(this, str, max)
|
||||
return mSearchHelper?.search(this, str, max)
|
||||
}
|
||||
|
||||
fun searchEntries(query: String): Cursor? {
|
||||
@@ -343,14 +356,14 @@ class Database {
|
||||
return entry
|
||||
}
|
||||
|
||||
@Throws(IOException::class, PwDbOutputException::class)
|
||||
@Throws(IOException::class, DatabaseOutputException::class)
|
||||
fun saveData(contentResolver: ContentResolver) {
|
||||
mUri?.let {
|
||||
saveData(contentResolver, it)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, PwDbOutputException::class)
|
||||
@Throws(IOException::class, DatabaseOutputException::class)
|
||||
private fun saveData(contentResolver: ContentResolver, uri: Uri) {
|
||||
val errorMessage = "Failed to store database."
|
||||
|
||||
@@ -484,7 +497,7 @@ class Database {
|
||||
?: false
|
||||
}
|
||||
|
||||
@Throws(InvalidKeyFileException::class, IOException::class)
|
||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
||||
fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) {
|
||||
pwDatabaseV3?.retrieveMasterKey(key, keyInputStream)
|
||||
pwDatabaseV4?.retrieveMasterKey(key, keyInputStream)
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.util.Log
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.KeyFileEmptyException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseKeyFileEmptyException
|
||||
import com.kunzisoft.keepass.utils.MemoryUtil
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
@@ -72,15 +72,15 @@ abstract class PwDatabase<Group : PwGroup<*, Group, Entry>, Entry : PwEntry<Grou
|
||||
|
||||
var rootGroup: Group? = null
|
||||
|
||||
@Throws(InvalidKeyFileException::class, IOException::class)
|
||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
||||
protected abstract fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray
|
||||
|
||||
@Throws(InvalidKeyFileException::class, IOException::class)
|
||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
||||
fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) {
|
||||
masterKey = getMasterKey(key, keyInputStream)
|
||||
}
|
||||
|
||||
@Throws(InvalidKeyFileException::class, IOException::class)
|
||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
||||
protected fun getCompositeKey(key: String, keyInputStream: InputStream): ByteArray {
|
||||
val fileKey = getFileKey(keyInputStream)
|
||||
val passwordKey = getPasswordKey(key)
|
||||
@@ -120,7 +120,7 @@ abstract class PwDatabase<Group : PwGroup<*, Group, Entry>, Entry : PwEntry<Grou
|
||||
return messageDigest.digest()
|
||||
}
|
||||
|
||||
@Throws(InvalidKeyFileException::class, IOException::class)
|
||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
||||
protected fun getFileKey(keyInputStream: InputStream): ByteArray {
|
||||
|
||||
val keyByteArrayOutputStream = ByteArrayOutputStream()
|
||||
@@ -134,7 +134,7 @@ abstract class PwDatabase<Group : PwGroup<*, Group, Entry>, Entry : PwEntry<Grou
|
||||
}
|
||||
|
||||
when (keyData.size.toLong()) {
|
||||
0L -> throw KeyFileEmptyException()
|
||||
0L -> throw LoadDatabaseKeyFileEmptyException()
|
||||
32L -> return keyData
|
||||
64L -> try {
|
||||
return hexStringToByteArray(String(keyData))
|
||||
@@ -238,7 +238,7 @@ abstract class PwDatabase<Group : PwGroup<*, Group, Entry>, Entry : PwEntry<Grou
|
||||
fun addGroupIndex(group: Group) {
|
||||
val groupId = group.nodeId
|
||||
if (groupIndexes.containsKey(groupId)) {
|
||||
Log.e(TAG, "Error, a group with the same UUID $groupId already exists")
|
||||
throw LoadDatabaseDuplicateUuidException(Type.GROUP, groupId)
|
||||
} else {
|
||||
this.groupIndexes[groupId] = group
|
||||
}
|
||||
@@ -273,8 +273,7 @@ abstract class PwDatabase<Group : PwGroup<*, Group, Entry>, Entry : PwEntry<Grou
|
||||
fun addEntryIndex(entry: Entry) {
|
||||
val entryId = entry.nodeId
|
||||
if (entryIndexes.containsKey(entryId)) {
|
||||
// TODO History
|
||||
Log.e(TAG, "Error, a group with the same UUID $entryId already exists, change the UUID")
|
||||
throw LoadDatabaseDuplicateUuidException(Type.ENTRY, entryId)
|
||||
} else {
|
||||
this.entryIndexes[entryId] = entry
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element
|
||||
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
||||
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
||||
import com.kunzisoft.keepass.stream.NullOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
@@ -112,7 +112,7 @@ class PwDatabaseV3 : PwDatabase<PwGroupV3, PwEntryV3>() {
|
||||
return newId
|
||||
}
|
||||
|
||||
@Throws(InvalidKeyFileException::class, IOException::class)
|
||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
||||
override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
||||
|
||||
return if (key != null && keyInputStream != null) {
|
||||
|
||||
@@ -27,7 +27,7 @@ import com.kunzisoft.keepass.crypto.CryptoUtil
|
||||
import com.kunzisoft.keepass.crypto.engine.AesEngine
|
||||
import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.*
|
||||
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
||||
import com.kunzisoft.keepass.utils.VariantDictionary
|
||||
import org.w3c.dom.Node
|
||||
@@ -236,7 +236,7 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
return getCustomData().isNotEmpty()
|
||||
}
|
||||
|
||||
@Throws(InvalidKeyFileException::class, IOException::class)
|
||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
||||
public override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
||||
|
||||
var masterKey = byteArrayOf()
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class ArcFourException : InvalidDBException() {
|
||||
companion object {
|
||||
private const val serialVersionUID = 2103983626687861237L
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
import android.net.Uri
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class ContentFileNotFoundException : FileNotFoundException() {
|
||||
companion object {
|
||||
fun getInstance(uri: Uri?): FileNotFoundException {
|
||||
if (uri == null) {
|
||||
return FileNotFoundException()
|
||||
}
|
||||
|
||||
val scheme = uri.scheme
|
||||
return if (scheme != null
|
||||
&& scheme.isNotEmpty()
|
||||
&& scheme.equals("content", ignoreCase = true)) {
|
||||
ContentFileNotFoundException()
|
||||
} else FileNotFoundException()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,10 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class PwDbOutputException : Exception {
|
||||
class DatabaseOutputException : Exception {
|
||||
constructor(string: String) : super(string)
|
||||
|
||||
constructor(string: String, e: Exception) : super(string, e)
|
||||
|
||||
constructor(e: Exception) : super(e)
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 3321212743159473368L
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class InvalidAlgorithmException : InvalidDBException() {
|
||||
companion object {
|
||||
private const val serialVersionUID = 3062682891863487208L
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
open class InvalidDBException : Exception {
|
||||
|
||||
constructor(str: String) : super(str)
|
||||
|
||||
constructor() : super()
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 5191964825154190923L
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class InvalidDBSignatureException : InvalidDBException() {
|
||||
companion object {
|
||||
private const val serialVersionUID = -5358923878743513758L
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class InvalidDBVersionException : InvalidDBException() {
|
||||
companion object {
|
||||
private const val serialVersionUID = -4260650987856400586L
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/package com.kunzisoft.keepass.database.exception
|
||||
|
||||
open class InvalidKeyFileException : InvalidDBException() {
|
||||
companion object {
|
||||
private const val serialVersionUID = 5540694419562294464L
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class InvalidPasswordException : InvalidDBException() {
|
||||
companion object {
|
||||
private const val serialVersionUID = -8729476180242058319L
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class KeyFileEmptyException : InvalidKeyFileException() {
|
||||
companion object {
|
||||
private const val serialVersionUID = -1630780661204212325L
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.StringRes
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.PwNodeId
|
||||
import com.kunzisoft.keepass.database.element.Type
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class LoadDatabaseArcFourException : LoadDatabaseException(R.string.error_arc4)
|
||||
|
||||
class LoadDatabaseFileNotFoundException : LoadDatabaseException(R.string.file_not_found_content)
|
||||
|
||||
class LoadDatabaseInvalidAlgorithmException : LoadDatabaseException(R.string.invalid_algorithm)
|
||||
|
||||
class LoadDatabaseDuplicateUuidException(type: Type, uuid: PwNodeId<*>):
|
||||
LoadDatabaseException("Error, a $type with the same UUID $uuid already exists")
|
||||
|
||||
class LoadDatabaseIOException(exception: IOException) : LoadDatabaseException(exception, R.string.error_load_database)
|
||||
|
||||
class LoadDatabaseKDFMemoryException(exception: IOException) : LoadDatabaseException(exception, R.string.error_load_database_KDF_memory)
|
||||
|
||||
class LoadDatabaseSignatureException : LoadDatabaseException(R.string.invalid_db_sig)
|
||||
|
||||
class LoadDatabaseVersionException : LoadDatabaseException(R.string.unsupported_db_version)
|
||||
|
||||
open class LoadDatabaseInvalidKeyFileException : LoadDatabaseException(R.string.keyfile_does_not_exist)
|
||||
|
||||
class LoadDatabaseInvalidPasswordException : LoadDatabaseException(R.string.invalid_password)
|
||||
|
||||
class LoadDatabaseKeyFileEmptyException : LoadDatabaseException(R.string.keyfile_is_empty)
|
||||
|
||||
class LoadDatabaseNoMemoryException(exception: OutOfMemoryError) : LoadDatabaseException(exception, R.string.error_out_of_memory)
|
||||
|
||||
open class LoadDatabaseException : Exception {
|
||||
|
||||
@StringRes
|
||||
var errorId: Int = R.string.error_load_database
|
||||
|
||||
constructor(errorMessage: String) : super(errorMessage)
|
||||
|
||||
constructor(errorMessageId: Int) : super() {
|
||||
errorId = errorMessageId
|
||||
}
|
||||
|
||||
constructor(throwable: Throwable, errorMessageId: Int? = null) : super(throwable) {
|
||||
errorMessageId?.let {
|
||||
errorId = it
|
||||
}
|
||||
}
|
||||
|
||||
constructor() : super()
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 5191964825154190923L
|
||||
}
|
||||
|
||||
fun getLocalizedMessage(resources: Resources): String {
|
||||
return resources.getString(errorId)
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,4 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.exception
|
||||
|
||||
class SamsungClipboardException(e: Exception) : Exception(e) {
|
||||
companion object {
|
||||
private const val serialVersionUID = -3168837280393843509L
|
||||
}
|
||||
|
||||
}
|
||||
class SamsungClipboardException(e: Exception) : Exception(e)
|
||||
|
||||
@@ -2,8 +2,4 @@ package com.kunzisoft.keepass.database.exception
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
class UnknownKDF : IOException(message) {
|
||||
companion object {
|
||||
private const val message = "Unknown key derivation function"
|
||||
}
|
||||
}
|
||||
class UnknownKDF : IOException("Unknown key derivation function")
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters
|
||||
import com.kunzisoft.keepass.database.NodeHandler
|
||||
import com.kunzisoft.keepass.database.element.*
|
||||
import com.kunzisoft.keepass.database.exception.InvalidDBVersionException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseVersionException
|
||||
import com.kunzisoft.keepass.stream.CopyInputStream
|
||||
import com.kunzisoft.keepass.stream.HmacBlockStream
|
||||
import com.kunzisoft.keepass.stream.LEDataInputStream
|
||||
@@ -130,9 +130,9 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
|
||||
/** Assumes the input stream is at the beginning of the .kdbx file
|
||||
* @param inputStream
|
||||
* @throws IOException
|
||||
* @throws InvalidDBVersionException
|
||||
* @throws LoadDatabaseVersionException
|
||||
*/
|
||||
@Throws(IOException::class, InvalidDBVersionException::class)
|
||||
@Throws(IOException::class, LoadDatabaseVersionException::class)
|
||||
fun loadFromFile(inputStream: InputStream): HeaderAndHash {
|
||||
val messageDigest: MessageDigest
|
||||
try {
|
||||
@@ -150,12 +150,12 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
|
||||
val sig2 = littleEndianDataInputStream.readInt()
|
||||
|
||||
if (!matchesHeader(sig1, sig2)) {
|
||||
throw InvalidDBVersionException()
|
||||
throw LoadDatabaseVersionException()
|
||||
}
|
||||
|
||||
version = littleEndianDataInputStream.readUInt() // Erase previous value
|
||||
if (!validVersion(version)) {
|
||||
throw InvalidDBVersionException()
|
||||
throw LoadDatabaseVersionException()
|
||||
}
|
||||
|
||||
var done = false
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
package com.kunzisoft.keepass.database.file.load
|
||||
|
||||
import com.kunzisoft.keepass.database.element.PwDatabase
|
||||
import com.kunzisoft.keepass.database.exception.InvalidDBException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
|
||||
import java.io.IOException
|
||||
@@ -36,9 +36,9 @@ abstract class Importer<PwDb : PwDatabase<*, *>> {
|
||||
* @return new PwDatabase container.
|
||||
*
|
||||
* @throws IOException on any file error.
|
||||
* @throws InvalidDBException on database error.
|
||||
* @throws LoadDatabaseException on database error.
|
||||
*/
|
||||
@Throws(IOException::class, InvalidDBException::class)
|
||||
@Throws(IOException::class, LoadDatabaseException::class)
|
||||
abstract fun openDatabase(databaseInputStream: InputStream,
|
||||
password: String?,
|
||||
keyInputStream: InputStream?,
|
||||
|
||||
@@ -73,7 +73,7 @@ class ImporterV3 : Importer<PwDatabaseV3>() {
|
||||
|
||||
private lateinit var mDatabaseToOpen: PwDatabaseV3
|
||||
|
||||
@Throws(IOException::class, InvalidDBException::class)
|
||||
@Throws(IOException::class, LoadDatabaseException::class)
|
||||
override fun openDatabase(databaseInputStream: InputStream,
|
||||
password: String?,
|
||||
keyInputStream: InputStream?,
|
||||
@@ -92,11 +92,11 @@ class ImporterV3 : Importer<PwDatabaseV3>() {
|
||||
hdr.loadFromFile(filebuf, 0)
|
||||
|
||||
if (hdr.signature1 != PwDbHeader.PWM_DBSIG_1 || hdr.signature2 != PwDbHeaderV3.DBSIG_2) {
|
||||
throw InvalidDBSignatureException()
|
||||
throw LoadDatabaseSignatureException()
|
||||
}
|
||||
|
||||
if (!hdr.matchesVersion()) {
|
||||
throw InvalidDBVersionException()
|
||||
throw LoadDatabaseVersionException()
|
||||
}
|
||||
|
||||
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
|
||||
@@ -109,7 +109,7 @@ class ImporterV3 : Importer<PwDatabaseV3>() {
|
||||
} else if (hdr.flags and PwDbHeaderV3.FLAG_TWOFISH != 0) {
|
||||
mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.Twofish
|
||||
} else {
|
||||
throw InvalidAlgorithmException()
|
||||
throw LoadDatabaseInvalidAlgorithmException()
|
||||
}
|
||||
|
||||
mDatabaseToOpen.numberKeyEncryptionRounds = hdr.numKeyEncRounds.toLong()
|
||||
@@ -152,7 +152,7 @@ class ImporterV3 : Importer<PwDatabaseV3>() {
|
||||
} catch (e1: IllegalBlockSizeException) {
|
||||
throw IOException("Invalid block size")
|
||||
} catch (e1: BadPaddingException) {
|
||||
throw InvalidPasswordException()
|
||||
throw LoadDatabaseInvalidPasswordException()
|
||||
}
|
||||
|
||||
val md: MessageDigest
|
||||
@@ -171,7 +171,7 @@ class ImporterV3 : Importer<PwDatabaseV3>() {
|
||||
if (!Arrays.equals(hash, hdr.contentsHash)) {
|
||||
|
||||
Log.w(TAG, "Database file did not decrypt correctly. (checksum code is broken)")
|
||||
throw InvalidPasswordException()
|
||||
throw LoadDatabaseInvalidPasswordException()
|
||||
}
|
||||
|
||||
// New manual root because V3 contains multiple root groups (here available with getRootGroups())
|
||||
|
||||
@@ -26,9 +26,9 @@ import com.kunzisoft.keepass.crypto.StreamCipherFactory
|
||||
import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
||||
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
|
||||
import com.kunzisoft.keepass.database.element.*
|
||||
import com.kunzisoft.keepass.database.exception.ArcFourException
|
||||
import com.kunzisoft.keepass.database.exception.InvalidDBException
|
||||
import com.kunzisoft.keepass.database.exception.InvalidPasswordException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseArcFourException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidPasswordException
|
||||
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
|
||||
import com.kunzisoft.keepass.database.element.security.ProtectedBinary
|
||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||
@@ -89,7 +89,7 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
private var entryCustomDataKey: String? = null
|
||||
private var entryCustomDataValue: String? = null
|
||||
|
||||
@Throws(IOException::class, InvalidDBException::class)
|
||||
@Throws(IOException::class, LoadDatabaseException::class)
|
||||
override fun openDatabase(databaseInputStream: InputStream,
|
||||
password: String?,
|
||||
keyInputStream: InputStream?,
|
||||
@@ -138,14 +138,14 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
try {
|
||||
storedStartBytes = dataDecrypted.readBytes(32)
|
||||
if (storedStartBytes == null || storedStartBytes.size != 32) {
|
||||
throw InvalidPasswordException()
|
||||
throw LoadDatabaseInvalidPasswordException()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw InvalidPasswordException()
|
||||
throw LoadDatabaseInvalidPasswordException()
|
||||
}
|
||||
|
||||
if (!Arrays.equals(storedStartBytes, header.streamStartBytes)) {
|
||||
throw InvalidPasswordException()
|
||||
throw LoadDatabaseInvalidPasswordException()
|
||||
}
|
||||
|
||||
isPlain = HashedBlockInputStream(dataDecrypted)
|
||||
@@ -153,18 +153,18 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
val isData = LEDataInputStream(databaseInputStream)
|
||||
val storedHash = isData.readBytes(32)
|
||||
if (!Arrays.equals(storedHash, hashOfHeader)) {
|
||||
throw InvalidDBException()
|
||||
throw LoadDatabaseException()
|
||||
}
|
||||
|
||||
val hmacKey = mDatabase.hmacKey ?: throw InvalidDBException()
|
||||
val hmacKey = mDatabase.hmacKey ?: throw LoadDatabaseException()
|
||||
val headerHmac = PwDbHeaderV4.computeHeaderHmac(pbHeader, hmacKey)
|
||||
val storedHmac = isData.readBytes(32)
|
||||
if (storedHmac == null || storedHmac.size != 32) {
|
||||
throw InvalidDBException()
|
||||
throw LoadDatabaseException()
|
||||
}
|
||||
// Mac doesn't match
|
||||
if (!Arrays.equals(headerHmac, storedHmac)) {
|
||||
throw InvalidDBException()
|
||||
throw LoadDatabaseException()
|
||||
}
|
||||
|
||||
val hmIs = HmacBlockInputStream(isData, true, hmacKey)
|
||||
@@ -185,7 +185,7 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
|
||||
|
||||
if (randomStream == null) {
|
||||
throw ArcFourException()
|
||||
throw LoadDatabaseArcFourException()
|
||||
}
|
||||
|
||||
readXmlStreamed(isXml)
|
||||
@@ -270,7 +270,7 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
Binaries
|
||||
}
|
||||
|
||||
@Throws(IOException::class, InvalidDBException::class)
|
||||
@Throws(IOException::class, LoadDatabaseException::class)
|
||||
private fun readXmlStreamed(readerStream: InputStream) {
|
||||
try {
|
||||
readDocumentStreamed(createPullParser(readerStream))
|
||||
@@ -281,7 +281,7 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
|
||||
}
|
||||
|
||||
@Throws(XmlPullParserException::class, IOException::class, InvalidDBException::class)
|
||||
@Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
|
||||
private fun readDocumentStreamed(xpp: XmlPullParser) {
|
||||
|
||||
ctxGroups.clear()
|
||||
@@ -312,7 +312,7 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
if (ctxGroups.size != 0) throw IOException("Malformed")
|
||||
}
|
||||
|
||||
@Throws(XmlPullParserException::class, IOException::class, InvalidDBException::class)
|
||||
@Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
|
||||
private fun readXmlElement(ctx: KdbContext, xpp: XmlPullParser): KdbContext {
|
||||
val name = xpp.name
|
||||
when (ctx) {
|
||||
@@ -336,7 +336,7 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
|
||||
if (encodedHash.isNotEmpty() && hashOfHeader != null) {
|
||||
val hash = Base64Coder.decode(encodedHash)
|
||||
if (!Arrays.equals(hash, hashOfHeader)) {
|
||||
throw InvalidDBException()
|
||||
throw LoadDatabaseException()
|
||||
}
|
||||
}
|
||||
} else if (name.equals(PwDatabaseV4XML.ElemSettingsChanged, ignoreCase = true)) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters
|
||||
import com.kunzisoft.keepass.database.element.PwDatabaseV4
|
||||
import com.kunzisoft.keepass.database.file.PwDbHeader
|
||||
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
import com.kunzisoft.keepass.stream.HmacBlockStream
|
||||
import com.kunzisoft.keepass.stream.LEDataOutputStream
|
||||
import com.kunzisoft.keepass.stream.MacOutputStream
|
||||
@@ -41,7 +41,7 @@ import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class PwDbHeaderOutputV4 @Throws(PwDbOutputException::class)
|
||||
class PwDbHeaderOutputV4 @Throws(DatabaseOutputException::class)
|
||||
constructor(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os: OutputStream) : PwDbHeaderOutput() {
|
||||
private val los: LEDataOutputStream
|
||||
private val mos: MacOutputStream
|
||||
@@ -54,13 +54,13 @@ constructor(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os:
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-256")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw PwDbOutputException("SHA-256 not implemented here.")
|
||||
throw DatabaseOutputException("SHA-256 not implemented here.")
|
||||
}
|
||||
|
||||
try {
|
||||
db.makeFinalKey(header.masterSeed)
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
}
|
||||
|
||||
val hmac: Mac
|
||||
@@ -69,9 +69,9 @@ constructor(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os:
|
||||
val signingKey = SecretKeySpec(HmacBlockStream.GetHmacKey64(db.hmacKey, Types.ULONG_MAX_VALUE), "HmacSHA256")
|
||||
hmac.init(signingKey)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
}
|
||||
|
||||
dos = DigestOutputStream(os, md)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
package com.kunzisoft.keepass.database.file.save
|
||||
|
||||
import com.kunzisoft.keepass.database.file.PwDbHeader
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
|
||||
import java.io.OutputStream
|
||||
import java.security.NoSuchAlgorithmException
|
||||
@@ -28,13 +28,13 @@ import java.security.SecureRandom
|
||||
|
||||
abstract class PwDbOutput<Header : PwDbHeader> protected constructor(protected var mOS: OutputStream) {
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
protected open fun setIVs(header: Header): SecureRandom {
|
||||
val random: SecureRandom
|
||||
try {
|
||||
random = SecureRandom.getInstance("SHA1PRNG")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw PwDbOutputException("Does not support secure random number generation.")
|
||||
throw DatabaseOutputException("Does not support secure random number generation.")
|
||||
}
|
||||
|
||||
random.nextBytes(header.encryptionIV)
|
||||
@@ -43,10 +43,10 @@ abstract class PwDbOutput<Header : PwDbHeader> protected constructor(protected v
|
||||
return random
|
||||
}
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
abstract fun output()
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
abstract fun outputHeader(outputStream: OutputStream): Header
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.file.save
|
||||
|
||||
import com.kunzisoft.keepass.crypto.CipherFactory
|
||||
import com.kunzisoft.keepass.database.element.*
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
import com.kunzisoft.keepass.database.file.PwDbHeader
|
||||
import com.kunzisoft.keepass.database.file.PwDbHeaderV3
|
||||
import com.kunzisoft.keepass.stream.LEDataOutputStream
|
||||
@@ -42,18 +42,18 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
|
||||
private var headerHashBlock: ByteArray? = null
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
fun getFinalKey(header: PwDbHeader): ByteArray? {
|
||||
try {
|
||||
val h3 = header as PwDbHeaderV3
|
||||
mDatabaseV3.makeFinalKey(h3.masterSeed, h3.transformSeed, mDatabaseV3.numberKeyEncryptionRounds)
|
||||
return mDatabaseV3.finalKey
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException("Key creation failed.", e)
|
||||
throw DatabaseOutputException("Key creation failed.", e)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
override fun output() {
|
||||
// Before we output the header, we should sort our list of groups
|
||||
// and remove any orphaned nodes that are no longer part of the tree hierarchy
|
||||
@@ -74,7 +74,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
throw Exception()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw PwDbOutputException("Algorithm not supported.", e)
|
||||
throw DatabaseOutputException("Algorithm not supported.", e)
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -86,23 +86,23 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
bos.close()
|
||||
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw PwDbOutputException("Invalid key", e)
|
||||
throw DatabaseOutputException("Invalid key", e)
|
||||
} catch (e: InvalidAlgorithmParameterException) {
|
||||
throw PwDbOutputException("Invalid algorithm parameter.", e)
|
||||
throw DatabaseOutputException("Invalid algorithm parameter.", e)
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException("Failed to output final encrypted part.", e)
|
||||
throw DatabaseOutputException("Failed to output final encrypted part.", e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
override fun setIVs(header: PwDbHeaderV3): SecureRandom {
|
||||
val random = super.setIVs(header)
|
||||
random.nextBytes(header.transformSeed)
|
||||
return random
|
||||
}
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
override fun outputHeader(outputStream: OutputStream): PwDbHeaderV3 {
|
||||
// Build header
|
||||
val header = PwDbHeaderV3()
|
||||
@@ -115,7 +115,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
} else if (mDatabaseV3.encryptionAlgorithm === PwEncryptionAlgorithm.Twofish) {
|
||||
header.flags = header.flags or PwDbHeaderV3.FLAG_TWOFISH
|
||||
} else {
|
||||
throw PwDbOutputException("Unsupported algorithm.")
|
||||
throw DatabaseOutputException("Unsupported algorithm.")
|
||||
}
|
||||
|
||||
header.version = PwDbHeaderV3.DBVER_DW
|
||||
@@ -130,7 +130,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance("SHA-256")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw PwDbOutputException("SHA-256 not implemented here.", e)
|
||||
throw DatabaseOutputException("SHA-256 not implemented here.", e)
|
||||
}
|
||||
|
||||
// Header checksum
|
||||
@@ -138,7 +138,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
try {
|
||||
headerDigest = MessageDigest.getInstance("SHA-256")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw PwDbOutputException("SHA-256 not implemented here.", e)
|
||||
throw DatabaseOutputException("SHA-256 not implemented here.", e)
|
||||
}
|
||||
|
||||
var nos = NullOutputStream()
|
||||
@@ -151,7 +151,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
pho.outputEnd()
|
||||
headerDos.flush()
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
}
|
||||
|
||||
val headerHash = headerDigest.digest()
|
||||
@@ -166,7 +166,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
bos.flush()
|
||||
bos.close()
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException("Failed to generate checksum.", e)
|
||||
throw DatabaseOutputException("Failed to generate checksum.", e)
|
||||
}
|
||||
|
||||
header.contentsHash = messageDigest!!.digest()
|
||||
@@ -181,14 +181,14 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
pho.outputEnd()
|
||||
dos.flush()
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
}
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
@Suppress("CAST_NEVER_SUCCEEDS")
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
fun outputPlanGroupAndEntries(os: OutputStream) {
|
||||
val los = LEDataOutputStream(os)
|
||||
|
||||
@@ -199,7 +199,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
los.writeInt(headerHashBlock!!.size)
|
||||
los.write(headerHashBlock!!)
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException("Failed to output header hash.", e)
|
||||
throw DatabaseOutputException("Failed to output header hash.", e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
try {
|
||||
pgo.output()
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException("Failed to output a tree", e)
|
||||
throw DatabaseOutputException("Failed to output a tree", e)
|
||||
}
|
||||
}
|
||||
mDatabaseV3.doForEachEntryInIndex { entry ->
|
||||
@@ -217,7 +217,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
|
||||
try {
|
||||
peo.output()
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException("Failed to output an entry.", e)
|
||||
throw DatabaseOutputException("Failed to output an entry.", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
||||
import com.kunzisoft.keepass.database.*
|
||||
import com.kunzisoft.keepass.database.element.*
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
||||
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
|
||||
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
|
||||
@@ -63,14 +63,14 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
private var headerHmac: ByteArray? = null
|
||||
private var engine: CipherEngine? = null
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
override fun output() {
|
||||
|
||||
try {
|
||||
try {
|
||||
engine = CipherFactory.getInstance(mDatabaseV4.dataCipher)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw PwDbOutputException("No such cipher", e)
|
||||
throw DatabaseOutputException("No such cipher", e)
|
||||
}
|
||||
|
||||
header = outputHeader(mOS)
|
||||
@@ -104,13 +104,13 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
outputDatabase(osXml)
|
||||
osXml.close()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
} catch (e: IllegalStateException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
}
|
||||
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException(e)
|
||||
throw DatabaseOutputException(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
xml.endTag(null, PwDatabaseV4XML.ElemMeta)
|
||||
}
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
private fun attachStreamEncryptor(header: PwDbHeaderV4, os: OutputStream): CipherOutputStream {
|
||||
val cipher: Cipher
|
||||
try {
|
||||
@@ -236,13 +236,13 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
|
||||
cipher = engine!!.getCipher(Cipher.ENCRYPT_MODE, mDatabaseV4.finalKey!!, header.encryptionIV)
|
||||
} catch (e: Exception) {
|
||||
throw PwDbOutputException("Invalid algorithm.", e)
|
||||
throw DatabaseOutputException("Invalid algorithm.", e)
|
||||
}
|
||||
|
||||
return CipherOutputStream(os, cipher)
|
||||
}
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
override fun setIVs(header: PwDbHeaderV4): SecureRandom {
|
||||
val random = super.setIVs(header)
|
||||
random.nextBytes(header.masterSeed)
|
||||
@@ -275,7 +275,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
|
||||
randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
|
||||
if (randomStream == null) {
|
||||
throw PwDbOutputException("Invalid random cipher")
|
||||
throw DatabaseOutputException("Invalid random cipher")
|
||||
}
|
||||
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
@@ -285,7 +285,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
return random
|
||||
}
|
||||
|
||||
@Throws(PwDbOutputException::class)
|
||||
@Throws(DatabaseOutputException::class)
|
||||
override fun outputHeader(outputStream: OutputStream): PwDbHeaderV4 {
|
||||
|
||||
val header = PwDbHeaderV4(mDatabaseV4)
|
||||
@@ -295,7 +295,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
try {
|
||||
pho.output()
|
||||
} catch (e: IOException) {
|
||||
throw PwDbOutputException("Failed to output the header.", e)
|
||||
throw DatabaseOutputException("Failed to output the header.", e)
|
||||
}
|
||||
|
||||
hashOfHeader = pho.hashOfHeader
|
||||
|
||||
@@ -33,6 +33,12 @@ object PreferencesUtil {
|
||||
return prefs.getBoolean(context.getString(R.string.show_read_only_warning), true)
|
||||
}
|
||||
|
||||
fun rememberKeyFiles(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.keyfile_key),
|
||||
context.resources.getBoolean(R.bool.keyfile_default))
|
||||
}
|
||||
|
||||
fun omitBackup(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.omitbackup_key),
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
||||
|
||||
/**
|
||||
* Callback after a task is completed.
|
||||
@@ -52,8 +53,21 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
|
||||
* launch the nested action runnable if exists and finish,
|
||||
* else directly finish
|
||||
*/
|
||||
protected fun finishRun(isSuccess: Boolean, message: String? = null) {
|
||||
protected fun finishRun(isSuccess: Boolean,
|
||||
message: String? = null) {
|
||||
finishRun(isSuccess, null, message)
|
||||
}
|
||||
|
||||
/**
|
||||
* If [success] or [executeNestedActionIfResultFalse] true,
|
||||
* launch the nested action runnable if exists and finish,
|
||||
* else directly finish
|
||||
*/
|
||||
protected fun finishRun(isSuccess: Boolean,
|
||||
exception: LoadDatabaseException?,
|
||||
message: String? = null) {
|
||||
result.isSuccess = isSuccess
|
||||
result.exception = exception
|
||||
result.message = message
|
||||
if (isSuccess || executeNestedActionIfResultFalse) {
|
||||
execute()
|
||||
@@ -89,5 +103,8 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
|
||||
/**
|
||||
* Class to manage result from ActionRunnable
|
||||
*/
|
||||
data class Result(var isSuccess: Boolean = true, var message: String? = null, var data: Bundle? = null)
|
||||
data class Result(var isSuccess: Boolean = true,
|
||||
var message: String? = null,
|
||||
var exception: LoadDatabaseException? = null,
|
||||
var data: Bundle? = null)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
<string name="error_title_required">اكتب عنوانًا.</string>
|
||||
<string name="field_name">اسم الحقل</string>
|
||||
<string name="field_value">قيمة الحقل</string>
|
||||
<string name="file_not_found">تعذر إيجاد الملف.</string>
|
||||
<string name="generate_password">توليد كلمة سر</string>
|
||||
<string name="hint_conf_pass">تأكيد كلمة السر</string>
|
||||
<string name="hint_group_name">اسم المجموعة</string>
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
<string name="error_rounds_too_large">Massa passades. Establint a 2147483648.</string>
|
||||
<string name="error_title_required">És necessari un títol.</string>
|
||||
<string name="error_wrong_length">Insereix un enter positiu al camp longitud</string>
|
||||
<string name="file_not_found">Arxiu no trobat.</string>
|
||||
<string name="file_browser">Explorador d\'arxius</string>
|
||||
<string name="generate_password">Generar contrasenya</string>
|
||||
<string name="hint_conf_pass">confirma contrasenya</string>
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
<string name="error_wrong_length">Do nastavení „Délka“ zadejte celé kladné číslo.</string>
|
||||
<string name="field_name">Název pole</string>
|
||||
<string name="field_value">Hodnota pole</string>
|
||||
<string name="file_not_found">Soubor nenalezen.</string>
|
||||
<string name="file_browser">Správce souborů</string>
|
||||
<string name="generate_password">Vytvoř heslo</string>
|
||||
<string name="hint_conf_pass">potvrď heslo</string>
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
<string name="error_wrong_length">Angiv et positivt heltal i feltet \"Længde\".</string>
|
||||
<string name="field_name">Feltnavn</string>
|
||||
<string name="field_value">Feltværdi</string>
|
||||
<string name="file_not_found">Kunne ikke finde filen.</string>
|
||||
<string name="file_browser">Filhåndtering</string>
|
||||
<string name="generate_password">Generer adgangskode</string>
|
||||
<string name="hint_conf_pass">bekræft adgangskode</string>
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
<string name="error_wrong_length">Eine positive ganze Zahl in das Feld „Länge“ eingeben.</string>
|
||||
<string name="field_name">Feldname</string>
|
||||
<string name="field_value">Feldwert</string>
|
||||
<string name="file_not_found">Datei nicht gefunden.</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="generate_password">Passwort generieren</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_wrong_length">Εισάγετε έναν θετικό ακέραιο αριθμό στο πεδίο μήκους</string>
|
||||
<string name="field_name">Όνομα Πεδίου</string>
|
||||
<string name="field_value">Τιμή πεδίου</string>
|
||||
<string name="file_not_found">Το αρχείο δεν βρέθηκε.</string>
|
||||
<string name="file_browser">Διαχείριση Αρχείων</string>
|
||||
<string name="generate_password">Δημιουργία Κωδικού</string>
|
||||
<string name="hint_conf_pass">επιβεβαίωση κωδικού</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_rounds_too_large">Pasadas demasiado grande. Establecido a 2147483648.</string>
|
||||
<string name="error_title_required">Se necesita un título.</string>
|
||||
<string name="error_wrong_length">Introduzca un entero positivo en el campo longitud</string>
|
||||
<string name="file_not_found">Archivo no encontrado.</string>
|
||||
<string name="file_browser">Explorador de Archivos</string>
|
||||
<string name="generate_password">Generar Contraseña</string>
|
||||
<string name="hint_conf_pass">confirmar contraseña</string>
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
<string name="error_wrong_length">Eremuaren luzeran entero positibo bat sartu</string>
|
||||
<string name="field_name">Eremuaren izena</string>
|
||||
<string name="field_value">Eremuaren balorea</string>
|
||||
<string name="file_not_found">Fitxategi ez aurkitua.</string>
|
||||
<string name="file_browser">Fitxategien nabigatzailea</string>
|
||||
<string name="generate_password">Pasahitza sortu</string>
|
||||
<string name="hint_conf_pass">pasahitza berretsi</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_wrong_length">Syötä positiivinen kokonaisluku pituus-kenttään</string>
|
||||
<string name="field_name">Kentän nimi</string>
|
||||
<string name="field_value">Kentän arvo</string>
|
||||
<string name="file_not_found">Tiedostoa ei löydetty.</string>
|
||||
<string name="file_browser">Tiedostoselain</string>
|
||||
<string name="generate_password">Generoi salasana</string>
|
||||
<string name="hint_conf_pass">vahvista salasana</string>
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
<string name="error_autofill_enable_service">Impossible d’activer le service de remplissage automatique.</string>
|
||||
<string name="field_name">Nom du champ</string>
|
||||
<string name="field_value">Valeur du champ</string>
|
||||
<string name="file_not_found">Impossible de trouver le fichier.</string>
|
||||
<string name="file_not_found_content">Impossible de trouver le fichier. Essayer de le rouvrir depuis votre gestionnaire de fichiers.</string>
|
||||
<string name="file_browser">Gestionnaire de fichiers</string>
|
||||
<string name="generate_password">Générer un mot de passe</string>
|
||||
|
||||
@@ -76,7 +76,6 @@
|
||||
<string name="error_wrong_length">Írjon be egy pozitív egész számot a „Hossz” mezőbe.</string>
|
||||
<string name="field_name">Mezőnév</string>
|
||||
<string name="field_value">Mezőérték</string>
|
||||
<string name="file_not_found">A fájl nem található.</string>
|
||||
<string name="file_not_found_content">A fájl nem található. Próbálja meg újra megnyitni a fájlkezelőben.</string>
|
||||
<string name="file_browser">Fájlkezelő</string>
|
||||
<string name="generate_password">Jelszó előállítása</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_wrong_length">Inserisci un numero naturale positivo nel campo \"lunghezza\".</string>
|
||||
<string name="field_name">Nome campo</string>
|
||||
<string name="field_value">Valore campo</string>
|
||||
<string name="file_not_found">File non trovato.</string>
|
||||
<string name="file_not_found_content">File non trovato. Prova a riaprirlo dal tuo gestore di file.</string>
|
||||
<string name="file_browser">Gestore file</string>
|
||||
<string name="generate_password">Genera password</string>
|
||||
|
||||
@@ -74,7 +74,6 @@
|
||||
<string name="error_wrong_length">הזן מספר חיובי בשדה האורך</string>
|
||||
<string name="field_name">שם השדה</string>
|
||||
<string name="field_value">ערך השדה</string>
|
||||
<string name="file_not_found">קובץ לא נמצא.</string>
|
||||
<string name="file_browser">סייר קבצים</string>
|
||||
<string name="generate_password">צור סיסמה</string>
|
||||
<string name="hint_conf_pass">אשר סיסמה</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_rounds_too_large">値が大きすぎます。 2147483648にセットしました。</string>
|
||||
<string name="error_title_required">タイトルは必須入力です。</string>
|
||||
<string name="error_wrong_length">\"長さ\"欄には正の整数を入力してください。</string>
|
||||
<string name="file_not_found">ファイルが見つかりません。</string>
|
||||
<string name="file_browser">ファイルブラウザ</string>
|
||||
<string name="generate_password">パスワードを生成する</string>
|
||||
<string name="hint_conf_pass">パスワードをもう一度入力</string>
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
<string name="error_move_folder_in_itself">그룹을 자신에게 옮길 수 없습니다.</string>
|
||||
<string name="field_name">필드 이름</string>
|
||||
<string name="field_value">필드 값</string>
|
||||
<string name="file_not_found">파일을 찾을 수 없습니다.</string>
|
||||
<string name="file_not_found_content">파일을 찾을 수 없습니다. 파일 탐색기에서 열리는지 확인해 주세요.</string>
|
||||
<string name="file_browser">파일 탐색기</string>
|
||||
<string name="generate_password">비밀번호 생성</string>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<resources>
|
||||
<string name="about_description">KeePass DX yra KeePass slaptažodžių tvarkyklės realizacija Android platformai</string>
|
||||
<string name="clipboard_cleared">Iškarpinė išvalyta.</string>
|
||||
<string name="file_not_found">Failas nerastas.</string>
|
||||
<string name="invalid_password">Neteisingas slaptažodis arba rakto failas.</string>
|
||||
<string name="about_feedback">Atsiliepimai:</string>
|
||||
<string name="about_homepage">Pagrindinis puslapis:</string>
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
<string name="error_wrong_length">Norādiet garumu lielāku par nulli</string>
|
||||
<string name="field_name">Lauka nosaukums</string>
|
||||
<string name="field_value">Lauka vērtība</string>
|
||||
<string name="file_not_found">Fails nav atrasts.</string>
|
||||
<string name="file_browser">Failu pārlūks</string>
|
||||
<string name="generate_password">Ģenerēt Paroli</string>
|
||||
<string name="hint_conf_pass">apstipriniet paroli</string>
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
<string name="error_move_folder_in_itself">Kan ikke flytte gruppe inn i seg selv.</string>
|
||||
<string name="field_name">Feltnavn</string>
|
||||
<string name="field_value">Feltverdi</string>
|
||||
<string name="file_not_found">Fant ikke filen.</string>
|
||||
<string name="file_not_found_content">Fant ikke filen. Prøv å åpne den fra din innholdsleverandør.</string>
|
||||
<string name="file_browser">Filutforsker</string>
|
||||
<string name="generate_password">Opprett passord</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_rounds_too_large">\"Cycli-waarde\" te groot. Wordt ingesteld op 2147483648.</string>
|
||||
<string name="error_title_required">Voeg een titel toe.</string>
|
||||
<string name="error_wrong_length">Voer een positief geheel getal in in het veld \"Lengte\".</string>
|
||||
<string name="file_not_found">Bestand niet gevonden.</string>
|
||||
<string name="file_browser">Bestandsverkenner</string>
|
||||
<string name="generate_password">Wachtwoord genereren</string>
|
||||
<string name="hint_conf_pass">wachtwoord bevestigen</string>
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<string name="error_rounds_too_large">For mange omgangar. Bruker 2147483648.</string>
|
||||
<string name="error_title_required">Treng ein tittel.</string>
|
||||
<string name="error_wrong_length">Bruk eit positivt heiltal i lengdfeltet</string>
|
||||
<string name="file_not_found">Fann ikkje fila.</string>
|
||||
<string name="file_browser">Filbehandlar</string>
|
||||
<string name="generate_password">Lag passord</string>
|
||||
<string name="hint_conf_pass">stadfest passordet</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_rounds_too_large">\"Rundy szyfrowania\" są zbyt wysokie. Ustaw na 2147483648.</string>
|
||||
<string name="error_title_required">Dodaj tytuł.</string>
|
||||
<string name="error_wrong_length">Wprowadź dodatnią liczbę całkowitą w polu \"Długość\".</string>
|
||||
<string name="file_not_found">Nie znaleziono pliku.</string>
|
||||
<string name="file_browser">Przeglądarka plików</string>
|
||||
<string name="generate_password">Generuj hasło</string>
|
||||
<string name="hint_conf_pass">potwierdź hasło</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_rounds_too_large">\"Número de rodadas\" é muito grande. Modificado para 2147483648.</string>
|
||||
<string name="error_title_required">Insira um título.</string>
|
||||
<string name="error_wrong_length">Digite um número inteiro positivo no campo \"Tamanho\".</string>
|
||||
<string name="file_not_found">Não pôde encontrar o arquivo.</string>
|
||||
<string name="file_browser">Localizador de arquivos</string>
|
||||
<string name="generate_password">Gerar senha</string>
|
||||
<string name="hint_conf_pass">confirmar senha</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_wrong_length">Digite um número inteiro positivo no campo \"Tamanho\".</string>
|
||||
<string name="field_name">Nome do campo</string>
|
||||
<string name="field_value">Valor do campo</string>
|
||||
<string name="file_not_found">Não pôde encontrar o ficheiro.</string>
|
||||
<string name="file_not_found_content">Arquivo não encontrado. Tente reabrí-lo de seu provedor de conteúdo.</string>
|
||||
<string name="file_browser">Localizador de ficheiros</string>
|
||||
<string name="generate_password">Gerar palavra-chave</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_wrong_length">Поле \"Длина\" должно быть положительным целым числом.</string>
|
||||
<string name="field_name">Название поля</string>
|
||||
<string name="field_value">Значение поля</string>
|
||||
<string name="file_not_found">Файл не найден.</string>
|
||||
<string name="file_not_found_content">Файл не найден. Попробуйте повторно открыть через встроенный поставщик содержимого.</string>
|
||||
<string name="file_browser">Обзор файлов</string>
|
||||
<string name="generate_password">Генерация пароля</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_rounds_too_large">Príliš veľa opakovaní. Nastavujem na 2147483648.</string>
|
||||
<string name="error_title_required">Vyžaduje sa názov.</string>
|
||||
<string name="error_wrong_length">Zadajte celé kladné číslo na dĺžku poľa</string>
|
||||
<string name="file_not_found">Súbor nenájdený.</string>
|
||||
<string name="file_browser">Správca Súborov</string>
|
||||
<string name="generate_password">Generovať Heslo</string>
|
||||
<string name="hint_conf_pass">potvrdiť heslo</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_wrong_length">Ange ett positivt heltal i fältet för längd</string>
|
||||
<string name="field_name">Fältnamn</string>
|
||||
<string name="field_value">Fältvärde</string>
|
||||
<string name="file_not_found">Filen hittades inte.</string>
|
||||
<string name="file_browser">Filhanterare</string>
|
||||
<string name="generate_password">Generera lösenord</string>
|
||||
<string name="hint_conf_pass">bekräfta lösenord</string>
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
<string name="error_move_folder_in_itself">Bir grubu kendine taşıyamazsın.</string>
|
||||
<string name="field_name">Alan adı</string>
|
||||
<string name="field_value">Alan değeri</string>
|
||||
<string name="file_not_found">Dosya bulunamadı.</string>
|
||||
<string name="file_not_found_content">Dosya bulunamadı. Dosya tarayıcınızda yeniden açmayı deneyin.</string>
|
||||
<string name="file_browser">Dosya tarayıcı</string>
|
||||
<string name="generate_password">Parola üret</string>
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<string name="error_rounds_too_large">Надто багато циклів. Установлено 2147483648.</string>
|
||||
<string name="error_title_required">Необхідно вказати заголовок.</string>
|
||||
<string name="error_wrong_length">Введіть ціле число на усю довжину поля</string>
|
||||
<string name="file_not_found">Файл не знайдено.</string>
|
||||
<string name="file_browser">Перегляд файлів</string>
|
||||
<string name="generate_password">Згенерувати пароль</string>
|
||||
<string name="hint_conf_pass">підтвердження пароля</string>
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<string name="error_rounds_too_large">“变换次数”过多。已设置为 2147483648。</string>
|
||||
<string name="error_title_required">请添加标题。</string>
|
||||
<string name="error_wrong_length">请在“长度”字段输入一个正整数。</string>
|
||||
<string name="file_not_found">找不到文件。</string>
|
||||
<string name="file_browser">文件浏览器</string>
|
||||
<string name="generate_password">生成密码</string>
|
||||
<string name="hint_conf_pass">确认密码</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_rounds_too_large">次數太多。最大設置到2147483648。</string>
|
||||
<string name="error_title_required">標題為必填。</string>
|
||||
<string name="error_wrong_length">長度欄位輸入一個正整數</string>
|
||||
<string name="file_not_found">文件未找到。</string>
|
||||
<string name="file_browser">檔案管理器</string>
|
||||
<string name="generate_password">生成密碼</string>
|
||||
<string name="hint_conf_pass">確認密碼</string>
|
||||
|
||||
@@ -113,7 +113,6 @@
|
||||
<string name="error_create_database_file">Unable to create database with this password and key file.</string>
|
||||
<string name="field_name">Field name</string>
|
||||
<string name="field_value">Field value</string>
|
||||
<string name="file_not_found">Could not find file.</string>
|
||||
<string name="file_not_found_content">Could not find file. Try reopening it from your file browser.</string>
|
||||
<string name="file_browser">File browser</string>
|
||||
<string name="generate_password">Generate password</string>
|
||||
|
||||
Reference in New Issue
Block a user