aboutsummaryrefslogtreecommitdiff
path: root/anastasis
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2023-10-13 20:34:15 -0600
committerIván Ávalos <avalos@disroot.org>2023-11-11 13:20:09 -0600
commit900669f6493f917120f6f7ab1e22c3b6f1ab64e2 (patch)
treeb277335c8131dce39085f3da17029638ef15fd75 /anastasis
parent54b15d71e7a0db687875d7ef75e42e7d7497168d (diff)
downloadtaler-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')
-rw-r--r--anastasis/src/main/java/net/taler/anastasis/reducers/ReducerManager.kt38
-rw-r--r--anastasis/src/main/java/net/taler/anastasis/shared/FileUtils.kt41
-rw-r--r--anastasis/src/main/java/net/taler/anastasis/ui/screens/backup/EditSecretScreen.kt61
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 ->