Refactorize uri util

This commit is contained in:
J-Jamet
2019-07-19 23:36:36 +02:00
parent 38abb9ca49
commit fcd3f7c3fe
13 changed files with 203 additions and 217 deletions

View File

@@ -169,8 +169,8 @@ class FileDatabaseSelectActivity : StylishActivity(),
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "")
if (fileName!!.isNotEmpty()) {
val dbUri = UriUtil.parseDefaultFile(fileName)
if (fileName != null && fileName.isNotEmpty()) {
val dbUri = UriUtil.parseUriFile(fileName)
var scheme: String? = null
if (dbUri != null)
scheme = dbUri.scheme
@@ -408,17 +408,20 @@ class FileDatabaseSelectActivity : StylishActivity(),
ProgressDialogThread(this@FileDatabaseSelectActivity,
{
CreateDatabaseRunnable(databaseFilename) { database ->
// TODO store database created
AssignPasswordInDatabaseRunnable(
this@FileDatabaseSelectActivity,
database,
masterPasswordChecked,
masterPassword,
keyFileChecked,
keyFile,
true, // TODO get readonly
LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename))
)
UriUtil.parseUriFile(databaseFilename)?.let { databaseUri ->
// TODO store database created
AssignPasswordInDatabaseRunnable(
this@FileDatabaseSelectActivity,
database,
masterPasswordChecked,
masterPassword,
keyFileChecked,
keyFile,
true, // TODO get readonly
LaunchGroupActivityFinish(databaseUri)
)
}
}
},
R.string.progress_create)

View File

@@ -265,7 +265,7 @@ class PasswordActivity : StylishActivity(),
val defaultFilename = prefs?.getString(KEY_DEFAULT_FILENAME, "")
if (mDatabaseFileUri != null
&& mDatabaseFileUri!!.path != null && mDatabaseFileUri!!.path!!.isNotEmpty()
&& UriUtil.equalsDefaultfile(mDatabaseFileUri, defaultFilename)) {
&& mDatabaseFileUri == UriUtil.parseUriFile(defaultFilename)) {
checkboxDefaultDatabaseView?.isChecked = true
}
@@ -562,8 +562,8 @@ class PasswordActivity : StylishActivity(),
private fun verifyAllViewsAndLoadDatabase() {
verifyCheckboxesAndLoadDatabase(
passwordView?.text.toString(),
UriUtil.parseDefaultFile(keyFileView?.text.toString()))
passwordView?.text?.toString(),
UriUtil.parseUriFile(keyFileView?.text?.toString()))
}
private fun verifyCheckboxesAndLoadDatabase(password: String?, keyFile: Uri?) {
@@ -579,8 +579,8 @@ class PasswordActivity : StylishActivity(),
}
private fun verifyKeyFileViewsAndLoadDatabase(password: String) {
val key = keyFileView?.text.toString()
var keyUri = UriUtil.parseDefaultFile(key)
val key = keyFileView?.text?.toString()
var keyUri = UriUtil.parseUriFile(key)
if (checkboxKeyFileView?.isChecked != true) {
keyUri = null
}
@@ -817,9 +817,8 @@ class PasswordActivity : StylishActivity(),
throw FileNotFoundException()
}
val uri = UriUtil.parseDefaultFile(fileName)
val scheme = uri.scheme
val uri = UriUtil.parseUriFile(fileName)
val scheme = uri?.scheme
if (scheme != null && scheme.isNotEmpty() && scheme.equals("file", ignoreCase = true)) {
val dbFile = File(uri.path!!)
if (!dbFile.exists()) {

View File

@@ -177,9 +177,8 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
private fun verifyFile(): Boolean {
var error = false
if (keyFileCheckBox != null
&& keyFileCheckBox!!.isChecked
&& keyFileView != null) {
val keyFile = UriUtil.parseDefaultFile(keyFileView!!.text.toString())
&& keyFileCheckBox!!.isChecked) {
val keyFile = UriUtil.parseUriFile(keyFileView?.text?.toString())
mKeyFile = keyFile
// Verify that a keyfile is set
@@ -228,11 +227,10 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data
) { uri ->
uri?.let { currentUri ->
UriUtil.parseDefaultFile(currentUri.toString())?.let { pathString ->
keyFileCheckBox?.isChecked = true
keyFileView?.text = pathString.toString()
}
UriUtil.parseUriFile(uri)?.let { pathUri ->
keyFileCheckBox?.isChecked = true
keyFileView?.text = pathUri.toString()
}
}
}

View File

@@ -142,7 +142,7 @@ class CreateFileDialogFragment : DialogFragment(), AdapterView.OnItemSelectedLis
// Spinner Drop down elements
val fileTypes = resources.getStringArray(R.array.file_types)
val dataAdapter = ArrayAdapter(activity!!, android.R.layout.simple_spinner_item, fileTypes)
val dataAdapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, fileTypes)
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = dataAdapter
// Or text if only one item https://github.com/Kunzisoft/KeePassDX/issues/105
@@ -158,7 +158,7 @@ class CreateFileDialogFragment : DialogFragment(), AdapterView.OnItemSelectedLis
val dialog = builder.create()
dialog.setOnShowListener { dialog1 ->
dialog.setOnShowListener { _ ->
positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE)
positiveButton?.setOnClickListener { _ ->
@@ -181,11 +181,13 @@ class CreateFileDialogFragment : DialogFragment(), AdapterView.OnItemSelectedLis
}
private fun buildPath(): Uri? {
if (folderPathView != null && mDatabaseFileExtension != null) {
if (folderPathView != null && fileNameView != null && mDatabaseFileExtension != null) {
var path = Uri.Builder().path(folderPathView!!.text.toString())
.appendPath(fileNameView!!.text.toString() + mDatabaseFileExtension!!)
.build()
path = UriUtil.translate(context, path)
context?.let { context ->
path = UriUtil.translateUri(context, path)
}
return path
}
return null

View File

@@ -68,7 +68,6 @@ class KeyFileHelper {
if (lookForOpenIntentsFilePicker(dataUri?.invoke()))
showBrowserDialog()
}
}
}
@@ -183,7 +182,7 @@ class KeyFileHelper {
val filename = data?.dataString
var keyUri: Uri? = null
if (filename != null) {
keyUri = UriUtil.parseDefaultFile(filename)
keyUri = UriUtil.parseUriFile(filename)
}
keyFileCallback?.invoke(keyUri)
}
@@ -194,7 +193,7 @@ class KeyFileHelper {
if (data != null) {
var uri = data.data
if (uri != null) {
if (activity != null && StorageAF.useStorageFramework(activity!!)) {
if (StorageAF.useStorageFramework(activity!!)) {
try {
// try to persist read and write permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@@ -208,7 +207,7 @@ class KeyFileHelper {
}
}
if (requestCode == GET_CONTENT) {
uri = UriUtil.translate(activity, uri)
uri = UriUtil.translateUri(activity!!, uri)
}
keyFileCallback?.invoke(uri)
}

View File

@@ -58,8 +58,8 @@ class UriIntentInitTask(private val weakContext: WeakReference<Context>,
}
} else {
databaseUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_FILENAME))
keyFileUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_KEYFILE))
databaseUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_FILENAME))
keyFileUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_KEYFILE))
if (keyFileUri == null || keyFileUri!!.toString().isEmpty()) {
keyFileUri = getKeyFileUri(databaseUri)

View File

@@ -24,7 +24,7 @@ import android.net.Uri
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.getUriInputStream
import com.kunzisoft.keepass.utils.UriUtil
import java.io.IOException
class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
@@ -57,7 +57,7 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
mBackupKey = ByteArray(database.masterKey.size)
System.arraycopy(database.masterKey, 0, mBackupKey!!, 0, mBackupKey!!.size)
val uriInputStream = getUriInputStream(context.contentResolver, mKeyFile)
val uriInputStream = UriUtil.getUriInputStream(context.contentResolver, mKeyFile)
database.retrieveMasterKey(mMasterPassword, uriInputStream)
// To save the database

View File

@@ -24,7 +24,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.tasks.ActionRunnable
class CreateDatabaseRunnable(private val mFilename: String,
val onDatabaseCreate: (database: Database) -> ActionRunnable)
val onDatabaseCreate: (database: Database) -> ActionRunnable?)
: ActionRunnable() {
override fun run() {
@@ -35,7 +35,7 @@ class CreateDatabaseRunnable(private val mFilename: String,
// Set Database state
loaded = true
// Commit changes
onDatabaseCreate(this).run()
onDatabaseCreate(this)?.run()
}
finishRun(true)

View File

@@ -44,7 +44,6 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.stream.LEDataInputStream
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.getUriInputStream
import org.apache.commons.io.FileUtils
import java.io.*
import java.util.*
@@ -196,7 +195,9 @@ class Database {
setDatabaseV4(databaseV4)
}
setUri(UriUtil.parseDefaultFile(databasePath))
UriUtil.parseUriFile(databasePath)?.let { uri ->
setUri(uri)
}
}
private fun setDatabaseV3(pwDatabaseV3: PwDatabaseV3) {
@@ -245,7 +246,7 @@ class Database {
// Pass Uris as InputStreams
val inputStream: InputStream?
try {
inputStream = getUriInputStream(ctx.contentResolver, uri)
inputStream = UriUtil.getUriInputStream(ctx.contentResolver, uri)
} catch (e: Exception) {
Log.e("KPD", "Database::loadData", e)
throw ContentFileNotFoundException.getInstance(uri)
@@ -255,7 +256,7 @@ class Database {
var keyFileInputStream: InputStream? = null
keyfile?.let {
try {
keyFileInputStream = getUriInputStream(ctx.contentResolver, keyfile)
keyFileInputStream = UriUtil.getUriInputStream(ctx.contentResolver, keyfile)
} catch (e: Exception) {
Log.e("KPD", "Database::loadData", e)
throw ContentFileNotFoundException.getInstance(keyfile)

View File

@@ -209,18 +209,16 @@ class FileDatabaseHistory private constructor(private val context: WeakReference
}
}
fun getKeyFileUriByDatabaseUri(database: Uri): Uri? {
if (!isEnabled) return null
fun getKeyFileUriByDatabaseUri(uri: Uri): Uri? {
if (!isEnabled)
return null
init()
val size = mDatabasesUriList.size
for (i in 0 until size) {
if (UriUtil.equalsDefaultfile(database, mDatabasesUriList[i])) {
return UriUtil.parseDefaultFile(mKeyFilesUriList[i])
if (uri == UriUtil.parseUriFile(mDatabasesUriList[i])) {
return UriUtil.parseUriFile(mKeyFilesUriList[i])
}
}
return null
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright 2017 Brian Pellin, 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.utils;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.kunzisoft.keepass.fileselect.StorageAF;
import java.io.File;
/**
* Created by bpellin on 3/5/16.
*/
public class UriUtil {
public static Uri parseDefaultFile(String text) {
if (text == null || text.isEmpty()) {
return null;
}
Uri uri = Uri.parse(text);
if (uri.getScheme() == null || uri.getScheme().isEmpty()) {
uri = uri.buildUpon().scheme("file").authority("").build();
}
return uri;
}
public static Uri parseDefaultFile(Uri uri) {
if (uri.getScheme() == null || uri.getScheme().isEmpty()) {
uri = uri.buildUpon().scheme("file").authority("").build();
}
return uri;
}
public static boolean equalsDefaultfile(Uri left, String right) {
left = parseDefaultFile(left);
Uri uriRight = parseDefaultFile(right);
return left.equals(uriRight);
}
/**
* Many android apps respond with non-writeable content URIs that correspond to files.
* This will attempt to translate the content URIs to file URIs when possible/appropriate
* @param uri
* @return
*/
public static Uri translate(Context ctx, Uri uri) {
// StorageAF provides nice URIs
if (StorageAF.INSTANCE.useStorageFramework(ctx) || hasWritableContentUri(uri)) { return uri; }
String scheme = uri.getScheme();
if (scheme == null || scheme.isEmpty()) { return uri; }
String filepath = null;
try {
// Use content resolver to try and find the file
if (scheme.equalsIgnoreCase("content")) {
Cursor cursor = ctx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null);
cursor.moveToFirst();
if (cursor != null) {
filepath = cursor.getString(0);
cursor.close();
if (!isValidFilePath(filepath)) {
filepath = null;
}
}
}
// Try using the URI path as a straight file
if (filepath == null || filepath.isEmpty()) {
filepath = uri.getEncodedPath();
if (!isValidFilePath(filepath)) {
filepath = null;
}
}
}
// Fall back to URI if this fails.
catch (Exception e) {
filepath = null;
}
// Update the file to a file URI
if (filepath != null && !filepath.isEmpty()) {
Uri.Builder b = new Uri.Builder();
uri = b.scheme("file").authority("").path(filepath).build();
}
return uri;
}
private static boolean isValidFilePath(String filepath) {
if (filepath == null || filepath.isEmpty()) { return false; }
File file = new File(filepath);
return file.exists() && file.canRead();
}
/**
* Whitelist for known content providers that support writing
* @param uri
* @return
*/
private static boolean hasWritableContentUri(Uri uri) {
String scheme = uri.getScheme();
if (scheme == null || scheme.isEmpty()) { return false; }
if (!scheme.equalsIgnoreCase("content")) { return false; }
switch (uri.getAuthority()) {
case "com.google.android.apps.docs.storage":
return true;
}
return false;
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.utils
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
import com.kunzisoft.keepass.fileselect.StorageAF
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.InputStream
object UriUtil {
fun parseUriFile(text: String?): Uri? {
if (text == null || text.isEmpty()) {
return null
}
return parseUriFile(Uri.parse(text))
}
fun parseUriFile(uri: Uri?): Uri? {
if (uri == null) {
return null
}
var currentUri = uri
if (currentUri.scheme == null || currentUri.scheme!!.isEmpty()) {
currentUri = currentUri.buildUpon().scheme("file").authority("").build()
}
return currentUri
}
/**
* Many android apps respond with non-writeable content URIs that correspond to files.
* This will attempt to translate the content URIs to file URIs when possible/appropriate
* @param uri
* @return
*/
fun translateUri(ctx: Context, uri: Uri): Uri {
var currentUri = uri
// StorageAF provides nice URIs
if (StorageAF.useStorageFramework(ctx) || hasWritableContentUri(currentUri)) {
return currentUri
}
val scheme = currentUri.scheme
if (scheme == null || scheme.isEmpty()) {
return currentUri
}
var filepath: String? = null
try {
// Use content resolver to try and find the file
if (scheme.equals("content", ignoreCase = true)) {
val cursor = ctx.contentResolver.query(currentUri, arrayOf(android.provider.MediaStore.Images.ImageColumns.DATA), null, null, null)
if (cursor != null) {
cursor.moveToFirst()
filepath = cursor.getString(0)
cursor.close()
if (!isValidFilePath(filepath)) {
filepath = null
}
}
}
// Try using the URI path as a straight file
if (filepath == null || filepath.isEmpty()) {
filepath = currentUri.encodedPath
if (!isValidFilePath(filepath)) {
filepath = null
}
}
} catch (e: Exception) {
filepath = null
}
// Fall back to URI if this fails.
// Update the file to a file URI
if (filepath != null && filepath.isNotEmpty()) {
val b = Uri.Builder()
currentUri = b.scheme("file").authority("").path(filepath).build()
}
return currentUri
}
private fun isValidFilePath(filepath: String?): Boolean {
if (filepath == null || filepath.isEmpty()) {
return false
}
val file = File(filepath)
return file.exists() && file.canRead()
}
/**
* Whitelist for known content providers that support writing
* @param uri
* @return
*/
private fun hasWritableContentUri(uri: Uri): Boolean {
val scheme = uri.scheme
if (scheme == null || scheme.isEmpty()) {
return false
}
if (!scheme.equals("content", ignoreCase = true)) {
return false
}
when (uri.authority) {
"com.google.android.apps.docs.storage" -> return true
}
return false
}
@Throws(FileNotFoundException::class)
fun getUriInputStream(contentResolver: ContentResolver, uri: Uri?): InputStream? {
if (uri == null)
return null
val scheme = uri.scheme
return if (scheme == null || scheme.isEmpty() || scheme == "file") {
FileInputStream(uri.path!!)
} else if (scheme == "content") {
contentResolver.openInputStream(uri)
} else {
null
}
}
}

View File

@@ -1,21 +0,0 @@
package com.kunzisoft.keepass.utils
import android.content.ContentResolver
import android.net.Uri
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.InputStream
@Throws(FileNotFoundException::class)
fun getUriInputStream(contentResolver: ContentResolver, uri: Uri?): InputStream? {
if (uri == null) return null
val scheme = uri.scheme
return if (scheme == null || scheme.isEmpty() || scheme == "file") {
FileInputStream(uri.path!!)
} else if (scheme == "content") {
contentResolver.openInputStream(uri)
} else {
null
}
}