diff options
author | Iván Ávalos <avalos@disroot.org> | 2023-10-13 20:34:15 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2023-11-11 13:20:09 -0600 |
commit | 900669f6493f917120f6f7ab1e22c3b6f1ab64e2 (patch) | |
tree | b277335c8131dce39085f3da17029638ef15fd75 /anastasis | |
parent | 54b15d71e7a0db687875d7ef75e42e7d7497168d (diff) | |
download | taler-android-900669f6493f917120f6f7ab1e22c3b6f1ab64e2.tar.gz taler-android-900669f6493f917120f6f7ab1e22c3b6f1ab64e2.tar.bz2 taler-android-900669f6493f917120f6f7ab1e22c3b6f1ab64e2.zip |
[anastasis] Improve I/O logic for file backups
Diffstat (limited to 'anastasis')
3 files changed, 90 insertions, 50 deletions
diff --git a/anastasis/src/main/java/net/taler/anastasis/reducers/ReducerManager.kt b/anastasis/src/main/java/net/taler/anastasis/reducers/ReducerManager.kt index 237a0a3..2b9eda6 100644 --- a/anastasis/src/main/java/net/taler/anastasis/reducers/ReducerManager.kt +++ b/anastasis/src/main/java/net/taler/anastasis/reducers/ReducerManager.kt @@ -34,11 +34,28 @@ import net.taler.anastasis.models.ContinentInfo import net.taler.anastasis.models.CoreSecret import net.taler.anastasis.models.CountryInfo import net.taler.anastasis.models.Policy +import net.taler.anastasis.models.ReducerArgs.AddAuthentication +import net.taler.anastasis.models.ReducerArgs.AddPolicy +import net.taler.anastasis.models.ReducerArgs.AddProvider +import net.taler.anastasis.models.ReducerArgs.DeleteAuthentication +import net.taler.anastasis.models.ReducerArgs.DeletePolicy +import net.taler.anastasis.models.ReducerArgs.DeleteProvider +import net.taler.anastasis.models.ReducerArgs.EnterSecret +import net.taler.anastasis.models.ReducerArgs.EnterSecretName +import net.taler.anastasis.models.ReducerArgs.EnterUserAttributes +import net.taler.anastasis.models.ReducerArgs.SelectChallenge +import net.taler.anastasis.models.ReducerArgs.SelectContinent +import net.taler.anastasis.models.ReducerArgs.SelectCountry +import net.taler.anastasis.models.ReducerArgs.SolveChallengeRequest +import net.taler.anastasis.models.ReducerArgs.UpdateExpiration +import net.taler.anastasis.models.ReducerArgs.UpdatePolicy import net.taler.anastasis.models.ReducerState +import net.taler.anastasis.shared.FileUtils.bufferedReadBytes import net.taler.anastasis.shared.Utils +import net.taler.common.CryptoUtils import net.taler.common.Timestamp +import java.io.InputStream import kotlin.time.Duration.Companion.seconds -import net.taler.anastasis.models.ReducerArgs.* class ReducerManager( private val state: MutableStateFlow<ReducerState?>, @@ -272,6 +289,25 @@ class ReducerManager( } } + fun enterFileSecret( + inputStream: InputStream?, + secret: CoreSecret, + expiration: Timestamp, + ) = scope.launch { + // TODO: better IO error handling + val bytes = inputStream?.bufferedReadBytes() ?: return@launch + val encodedBytes = CryptoUtils.encodeCrock(bytes) + state.value?.let { initialState -> + addTask(Tasks.Type.None) + api.reduceAction(initialState, "enter_secret", EnterSecret( + secret = secret.copy(value = encodedBytes), + expiration = expiration, + )) + .onSuccess { onSuccess(it) } + .onError { onError(it) } + } + } + fun updateSecretExpiration(expiration: Timestamp) = scope.launch { state.value?.let { initialState -> addTask(Tasks.Type.None) diff --git a/anastasis/src/main/java/net/taler/anastasis/shared/FileUtils.kt b/anastasis/src/main/java/net/taler/anastasis/shared/FileUtils.kt index 2626aa0..df66e69 100644 --- a/anastasis/src/main/java/net/taler/anastasis/shared/FileUtils.kt +++ b/anastasis/src/main/java/net/taler/anastasis/shared/FileUtils.kt @@ -20,26 +20,12 @@ import android.content.Context import android.net.Uri import android.provider.DocumentsContract import android.provider.OpenableColumns +import android.util.Log +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream object FileUtils { -// fun Context.createTempFileForDoc(uri: Uri): File { -// val storageDir = createTempDirectory() -// val filename = resolveDocFilename(uri) ?: UUID.randomUUID().toString() -// val file = File(storageDir.pathString, filename) -// if (file.exists()) file.delete() -// file.createNewFile() -// -// // Read file and copy to temp file -// val inputStream = contentResolver.openInputStream(uri) -// inputStream?.copyTo(file.outputStream()) -// inputStream?.close() -// -// // TODO: not the best solution! -// // Delete file on Java VM exit for security -// file.deleteOnExit() -// return file -// } - fun Context.resolveDocFilename(uri: Uri): String? = contentResolver.query(uri, null, null, null, null)?.use { cursor -> val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) @@ -53,4 +39,23 @@ object FileUtils { cursor.moveToFirst() cursor.getString(mimeIndex) } + + // Source: https://stackoverflow.com/a/12223201 + fun InputStream.bufferedReadBytes(): ByteArray? = use { + val buffer = ByteArrayOutputStream() + val data = ByteArray(16384) + var bytesRead: Int + try { + while (true) { + bytesRead = it.read(data, 0, data.size) + if (bytesRead == -1) break + buffer.write(data) + } + buffer.flush() + return buffer.toByteArray() + } catch (e: IOException) { + Log.d("FileUtils", e.stackTraceToString()) + return null + } + } }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/screens/backup/EditSecretScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/screens/backup/EditSecretScreen.kt index 937b5d3..70eb47d 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/screens/backup/EditSecretScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/screens/backup/EditSecretScreen.kt @@ -107,38 +107,37 @@ fun EditSecretScreen( onSecretEdited = { data -> secretData = data if (data !is SecretData.Empty) { - viewModel.reducerManager?.enterSecret( - secret = when (data) { - is SecretData.File -> { - val filename = context.resolveDocFilename(data.documentUri) - val mimeType = context.resolveDocMimeType(data.documentUri) - val inputStream = context.contentResolver.openInputStream(data.documentUri) - if (inputStream != null) { - val secret = CoreSecret( - // TODO: readBytes() has a 2GB limit - // TODO: make this SLOW readBytes() + encodeCrock operation async - value = CryptoUtils.encodeCrock(inputStream.readBytes()), - filename = filename, - mime = mimeType ?: "application/octet-stream", - ) - inputStream.close() - secret - } else { - error("couldn't open file") - } - } - is SecretData.PlainText -> CoreSecret( - value = CryptoUtils.encodeCrock(data.value.toByteArray(Charsets.UTF_8)), - mime = "text/plain", - ) - else -> error("impossible case") - }, - expiration = Timestamp.fromMillis( - secretExpirationDate - .toInstant(tz) - .toEpochMilliseconds() - ) + val expiration = Timestamp.fromMillis( + secretExpirationDate + .toInstant(tz) + .toEpochMilliseconds() ) + when (data) { + is SecretData.File -> { + val filename = context.resolveDocFilename(data.documentUri) + val mimeType = context.resolveDocMimeType(data.documentUri) + val inputStream = context.contentResolver.openInputStream(data.documentUri) + viewModel.reducerManager?.enterFileSecret( + inputStream = inputStream, + secret = CoreSecret( + value = "", // Doesn't matter + filename = filename, + mime = mimeType, + ), + expiration = expiration, + ) + } + is SecretData.PlainText -> { + viewModel.reducerManager?.enterSecret( + secret = CoreSecret( + value = CryptoUtils.encodeCrock(data.value.toByteArray(Charsets.UTF_8)), + mime = "text/plain", + ), + expiration = expiration, + ) + } + else -> {} // Impossible case + } } }, onExpirationEdited = { date -> |