Merge pull request #196 from ouchadam/feature/more-error-handling

More error handling
This commit is contained in:
Adam Brown 2022-10-09 16:07:21 +01:00 committed by GitHub
commit 65957f6854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 127 additions and 120 deletions

View File

@ -3,6 +3,13 @@ package app.dapk.st.core.extensions
inline fun <T> T?.ifNull(block: () -> T): T = this ?: block() inline fun <T> T?.ifNull(block: () -> T): T = this ?: block()
inline fun <T> ifOrNull(condition: Boolean, block: () -> T): T? = if (condition) block() else null inline fun <T> ifOrNull(condition: Boolean, block: () -> T): T? = if (condition) block() else null
inline fun <reified T> Any.takeAs(): T? {
return when (this) {
is T -> this
else -> null
}
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
inline fun <T, T1 : T, T2 : T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean, predicate2: (T) -> Boolean): Pair<T1, T2>? { inline fun <T, T1 : T, T2 : T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean, predicate2: (T) -> Boolean): Pair<T1, T2>? {
var firstValue: T1? = null var firstValue: T1? = null

View File

@ -1,21 +1,46 @@
package app.dapk.st.design.components package app.dapk.st.design.components
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable @Composable
fun GenericError(retryAction: () -> Unit) { fun GenericError(message: String = "Something went wrong...", label: String = "Retry", cause: Throwable? = null, action: () -> Unit) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { val moreDetails = cause?.let { "${it::class.java.simpleName}: ${it.message}" }
val openDetailsDialog = remember { mutableStateOf(false) }
if (openDetailsDialog.value) {
AlertDialog(
onDismissRequest = { openDetailsDialog.value = false },
confirmButton = {
Button(onClick = { openDetailsDialog.value = false }) {
Text("OK")
}
},
title = { Text("Details") },
text = {
Text(moreDetails!!)
}
)
}
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Something went wrong...") Text(message)
Button(onClick = { retryAction() }) { if (moreDetails != null) {
Text("Retry") Text("Tap for more details".uppercase(), fontSize = 12.sp, modifier = Modifier.clickable { openDetailsDialog.value = true }.padding(12.dp))
}
Spacer(modifier = Modifier.height(12.dp))
Button(onClick = { action() }) {
Text(label.uppercase())
} }
} }
} }

View File

@ -10,22 +10,27 @@ fun <T : Any> Spider(currentPage: SpiderPage<T>, onNavigate: (SpiderPage<out T>?
val pageCache = remember { mutableMapOf<Route<*>, SpiderPage<out T>>() } val pageCache = remember { mutableMapOf<Route<*>, SpiderPage<out T>>() }
pageCache[currentPage.route] = currentPage pageCache[currentPage.route] = currentPage
val navigateAndPopStack = {
pageCache.remove(currentPage.route)
onNavigate(pageCache[currentPage.parent])
}
val itemScope = object : SpiderItemScope {
override fun goBack() {
navigateAndPopStack()
}
}
val computedWeb = remember(true) { val computedWeb = remember(true) {
mutableMapOf<Route<*>, @Composable (T) -> Unit>().also { computedWeb -> mutableMapOf<Route<*>, @Composable (T) -> Unit>().also { computedWeb ->
val scope = object : SpiderScope { val scope = object : SpiderScope {
override fun <T> item(route: Route<T>, content: @Composable (T) -> Unit) { override fun <T> item(route: Route<T>, content: @Composable SpiderItemScope.(T) -> Unit) {
computedWeb[route] = { content(it as T) } computedWeb[route] = { content(itemScope, it as T) }
} }
} }
graph.invoke(scope) graph.invoke(scope)
} }
} }
val navigateAndPopStack = {
pageCache.remove(currentPage.route)
onNavigate(pageCache[currentPage.parent])
}
Column { Column {
if (currentPage.hasToolbar) { if (currentPage.hasToolbar) {
Toolbar( Toolbar(
@ -40,7 +45,11 @@ fun <T : Any> Spider(currentPage: SpiderPage<T>, onNavigate: (SpiderPage<out T>?
interface SpiderScope { interface SpiderScope {
fun <T> item(route: Route<T>, content: @Composable (T) -> Unit) fun <T> item(route: Route<T>, content: @Composable SpiderItemScope.(T) -> Unit)
}
interface SpiderItemScope {
fun goBack()
} }
data class SpiderPage<T>( data class SpiderPage<T>(

View File

@ -7,5 +7,6 @@ dependencies {
implementation project(":matrix:services:auth") implementation project(":matrix:services:auth")
implementation project(":matrix:services:profile") implementation project(":matrix:services:profile")
implementation project(":matrix:services:crypto") implementation project(":matrix:services:crypto")
implementation project(":design-library")
implementation project(":core") implementation project(":core")
} }

View File

@ -1,7 +1,6 @@
package app.dapk.st.login package app.dapk.st.login
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
@ -32,6 +31,8 @@ import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.dapk.st.core.StartObserving import app.dapk.st.core.StartObserving
import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.design.components.GenericError
import app.dapk.st.login.LoginEvent.LoginComplete import app.dapk.st.login.LoginEvent.LoginComplete
import app.dapk.st.login.LoginScreenState.* import app.dapk.st.login.LoginScreenState.*
@ -49,42 +50,8 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) {
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current
when (val state = loginViewModel.state) { when (val state = loginViewModel.state) {
is Error -> { is Error -> GenericError(cause = state.cause, action = { loginViewModel.start() })
val openDetailsDialog = remember { mutableStateOf(false) } Loading -> CenteredLoading()
if (openDetailsDialog.value) {
AlertDialog(
onDismissRequest = { openDetailsDialog.value = false },
confirmButton = {
Button(onClick = { openDetailsDialog.value = false }) {
Text("OK")
}
},
title = { Text("Details") },
text = {
Text(state.cause.message ?: "Unknown")
}
)
}
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Something went wrong")
Text("Tap for more details".uppercase(), fontSize = 12.sp, modifier = Modifier.clickable { openDetailsDialog.value = true }.padding(12.dp))
Spacer(modifier = Modifier.height(12.dp))
Button(onClick = {
loginViewModel.start()
}) {
Text("Retry".uppercase())
}
}
}
}
Loading -> {
Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
}
is Content -> is Content ->
Row { Row {

View File

@ -43,10 +43,7 @@ import app.dapk.st.core.LifecycleEffect
import app.dapk.st.core.StartObserving import app.dapk.st.core.StartObserving
import app.dapk.st.core.components.CenteredLoading import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.core.extensions.takeIfContent import app.dapk.st.core.extensions.takeIfContent
import app.dapk.st.design.components.MessengerUrlIcon import app.dapk.st.design.components.*
import app.dapk.st.design.components.MissingAvatarIcon
import app.dapk.st.design.components.SmallTalkTheme
import app.dapk.st.design.components.Toolbar
import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.UserId import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.sync.MessageMeta import app.dapk.st.matrix.sync.MessageMeta
@ -95,7 +92,7 @@ internal fun MessengerScreen(
}) })
when (state.composerState) { when (state.composerState) {
is ComposerState.Text -> { is ComposerState.Text -> {
Room(state.roomState, replyActions) Room(state.roomState, replyActions, onRetry = { viewModel.post(MessengerAction.OnMessengerVisible(roomId, attachments)) })
TextComposer( TextComposer(
state.composerState, state.composerState,
onTextChange = { viewModel.post(MessengerAction.ComposerTextUpdate(it)) }, onTextChange = { viewModel.post(MessengerAction.ComposerTextUpdate(it)) },
@ -132,7 +129,7 @@ private fun MessengerViewModel.ObserveEvents(galleryLauncher: ActivityResultLaun
} }
@Composable @Composable
private fun ColumnScope.Room(roomStateLce: Lce<MessengerState>, replyActions: ReplyActions) { private fun ColumnScope.Room(roomStateLce: Lce<MessengerState>, replyActions: ReplyActions, onRetry: () -> Unit) {
when (val state = roomStateLce) { when (val state = roomStateLce) {
is Lce.Loading -> CenteredLoading() is Lce.Loading -> CenteredLoading()
is Lce.Content -> { is Lce.Content -> {
@ -165,16 +162,7 @@ private fun ColumnScope.Room(roomStateLce: Lce<MessengerState>, replyActions: Re
} }
} }
is Lce.Error -> { is Lce.Error -> GenericError(cause = state.cause, action = onRetry)
Box(contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Something went wrong...")
Button(onClick = {}) {
Text("Retry")
}
}
}
}
} }
} }

View File

@ -15,6 +15,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.dapk.st.core.* import app.dapk.st.core.*
import app.dapk.st.core.extensions.unsafeLazy import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.design.components.GenericError
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -59,7 +60,10 @@ fun Activity.PermissionGuard(state: State<Lce<PermissionResult>>, onGranted: @Co
PermissionResult.ShowRational -> finish() PermissionResult.ShowRational -> finish()
} }
is Lce.Error -> finish() is Lce.Error -> GenericError(message = "Store permission required", label = "Close") {
finish()
}
is Lce.Loading -> { is Lce.Loading -> {
// loading should be quick, let's avoid displaying anything // loading should be quick, let's avoid displaying anything
} }

View File

@ -42,20 +42,17 @@ fun ImageGalleryScreen(viewModel: ImageGalleryViewModel, onTopLevelBack: () -> U
Spider(currentPage = viewModel.state.page, onNavigate = onNavigate) { Spider(currentPage = viewModel.state.page, onNavigate = onNavigate) {
item(ImageGalleryPage.Routes.folders) { item(ImageGalleryPage.Routes.folders) {
ImageGalleryFolders(it) { folder -> ImageGalleryFolders(it, onClick = { viewModel.selectFolder(it) }, onRetry = { viewModel.start() })
viewModel.selectFolder(folder)
}
} }
item(ImageGalleryPage.Routes.files) { item(ImageGalleryPage.Routes.files) {
ImageGalleryMedia(it, onImageSelected) ImageGalleryMedia(it, onImageSelected, onRetry = { viewModel.selectFolder(it.folder) })
} }
} }
} }
@Composable @Composable
fun ImageGalleryFolders(state: ImageGalleryPage.Folders, onClick: (Folder) -> Unit) { fun ImageGalleryFolders(state: ImageGalleryPage.Folders, onClick: (Folder) -> Unit, onRetry: () -> Unit) {
val screenWidth = LocalConfiguration.current.screenWidthDp val screenWidth = LocalConfiguration.current.screenWidthDp
val gradient = Brush.verticalGradient( val gradient = Brush.verticalGradient(
@ -106,12 +103,12 @@ fun ImageGalleryFolders(state: ImageGalleryPage.Folders, onClick: (Folder) -> Un
} }
} }
is Lce.Error -> GenericError { } is Lce.Error -> GenericError(cause = content.cause, action = onRetry)
} }
} }
@Composable @Composable
fun ImageGalleryMedia(state: ImageGalleryPage.Files, onFileSelected: (Media) -> Unit) { fun ImageGalleryMedia(state: ImageGalleryPage.Files, onFileSelected: (Media) -> Unit, onRetry: () -> Unit) {
val screenWidth = LocalConfiguration.current.screenWidthDp val screenWidth = LocalConfiguration.current.screenWidthDp
Column { Column {
@ -149,7 +146,7 @@ fun ImageGalleryMedia(state: ImageGalleryPage.Files, onFileSelected: (Media) ->
} }
} }
is Lce.Error -> GenericError { } is Lce.Error -> GenericError(cause = content.cause, action = onRetry)
} }
} }

View File

@ -48,7 +48,7 @@ class ImageGalleryViewModel(
route = ImageGalleryPage.Routes.files, route = ImageGalleryPage.Routes.files,
label = page.label, label = page.label,
parent = ImageGalleryPage.Routes.folders, parent = ImageGalleryPage.Routes.folders,
state = ImageGalleryPage.Files(Lce.Loading()) state = ImageGalleryPage.Files(Lce.Loading(), folder)
) )
) )
} }
@ -78,7 +78,7 @@ data class ImageGalleryState(
sealed interface ImageGalleryPage { sealed interface ImageGalleryPage {
data class Folders(val content: Lce<List<Folder>>) : ImageGalleryPage data class Folders(val content: Lce<List<Folder>>) : ImageGalleryPage
data class Files(val content: Lce<List<Media>>) : ImageGalleryPage data class Files(val content: Lce<List<Media>>, val folder: Folder) : ImageGalleryPage
object Routes { object Routes {
val folders = Route<Folders>("Folders") val folders = Route<Folders>("Folders")

View File

@ -119,7 +119,7 @@ private fun ProfilePage(context: Context, viewModel: ProfileViewModel, profile:
} }
@Composable @Composable
private fun Invitations(viewModel: ProfileViewModel, invitations: Page.Invitations) { private fun SpiderItemScope.Invitations(viewModel: ProfileViewModel, invitations: Page.Invitations) {
when (val state = invitations.content) { when (val state = invitations.content) {
is Lce.Loading -> CenteredLoading() is Lce.Loading -> CenteredLoading()
is Lce.Content -> { is Lce.Content -> {
@ -147,7 +147,7 @@ private fun Invitations(viewModel: ProfileViewModel, invitations: Page.Invitatio
} }
} }
is Lce.Error -> TODO() is Lce.Error -> GenericError(label = "Go back", cause = state.cause) { goBack() }
} }
} }

View File

@ -31,7 +31,6 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@ -40,6 +39,7 @@ import app.dapk.st.core.Lce
import app.dapk.st.core.StartObserving import app.dapk.st.core.StartObserving
import app.dapk.st.core.components.CenteredLoading import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.core.components.Header import app.dapk.st.core.components.Header
import app.dapk.st.core.extensions.takeAs
import app.dapk.st.core.getActivity import app.dapk.st.core.getActivity
import app.dapk.st.design.components.* import app.dapk.st.design.components.*
import app.dapk.st.matrix.crypto.ImportResult import app.dapk.st.matrix.crypto.ImportResult
@ -63,7 +63,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit,
} }
Spider(currentPage = viewModel.state.page, onNavigate = onNavigate) { Spider(currentPage = viewModel.state.page, onNavigate = onNavigate) {
item(Page.Routes.root) { item(Page.Routes.root) {
RootSettings(it) { viewModel.onClick(it) } RootSettings(it, onClick = { viewModel.onClick(it) }, onRetry = { viewModel.start() })
} }
item(Page.Routes.encryption) { item(Page.Routes.encryption) {
Encryption(viewModel, it) Encryption(viewModel, it)
@ -149,21 +149,19 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit,
} }
is ImportResult.Error -> { is ImportResult.Error -> {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { val message = when (result.cause) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { ImportResult.Error.Type.NoKeysFound -> "No keys found in the file"
val message = when (val type = result.cause) { ImportResult.Error.Type.UnexpectedDecryptionOutput -> "Unable to decrypt file, double check your passphrase"
ImportResult.Error.Type.NoKeysFound -> "No keys found in the file" is ImportResult.Error.Type.Unknown -> "Unknown error"
ImportResult.Error.Type.UnexpectedDecryptionOutput -> "Unable to decrypt file, double check your passphrase" ImportResult.Error.Type.UnableToOpenFile -> "Unable to open file"
is ImportResult.Error.Type.Unknown -> "${type.cause::class.java.simpleName}: ${type.cause.message}" ImportResult.Error.Type.InvalidFile -> "Unable to process file"
ImportResult.Error.Type.UnableToOpenFile -> "Unable to open file" }
} GenericError(
message = message,
Text(text = "Import failed\n$message", textAlign = TextAlign.Center) label = "Close",
Spacer(modifier = Modifier.height(12.dp)) cause = result.cause.takeAs<ImportResult.Error.Type.Unknown>()?.cause
Button(onClick = { navigator.navigate.upToHome() }) { ) {
Text(text = "Close".uppercase()) navigator.navigate.upToHome()
}
}
} }
} }
@ -182,7 +180,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit,
} }
@Composable @Composable
private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) { private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit, onRetry: () -> Unit) {
when (val content = page.content) { when (val content = page.content) {
is Lce.Content -> { is Lce.Content -> {
LazyColumn( LazyColumn(
@ -228,12 +226,10 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) {
} }
} }
is Lce.Error -> { is Lce.Error -> GenericError(cause = content.cause, action = onRetry)
// TODO
}
is Lce.Loading -> { is Lce.Loading -> {
// TODO // Should be quick enough to avoid needing a loading state
} }
} }
} }
@ -266,7 +262,7 @@ private fun PushProviders(viewModel: SettingsViewModel, state: Page.PushProvider
} }
} }
is Lce.Error -> TODO() is Lce.Error -> GenericError(cause = lce.cause) { viewModel.fetchPushProviders() }
} }
} }

View File

@ -14,6 +14,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.dapk.st.core.AppLogTag import app.dapk.st.core.AppLogTag
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.design.components.GenericError
import app.dapk.st.matrix.common.MatrixLogTag import app.dapk.st.matrix.common.MatrixLogTag
private val filterItems = listOf<String?>(null) + (MatrixLogTag.values().map { it.key } + AppLogTag.values().map { it.key }).distinct() private val filterItems = listOf<String?>(null) + (MatrixLogTag.values().map { it.key } + AppLogTag.values().map { it.key }).distinct()
@ -33,11 +35,13 @@ fun EventLogScreen(viewModel: EventLoggerViewModel) {
viewModel.selectLog(it, filter = null) viewModel.selectLog(it, filter = null)
} }
} }
else -> { else -> {
Events( Events(
selectedPageContent = state.selectedState, selectedPageContent = state.selectedState,
onExit = { viewModel.exitLog() }, onExit = { viewModel.exitLog() },
onSelectTag = { viewModel.selectLog(state.selectedState.selectedPage, it) } onSelectTag = { viewModel.selectLog(state.selectedState.selectedPage, it) },
onRetry = { viewModel.start() },
) )
} }
} }
@ -46,6 +50,7 @@ fun EventLogScreen(viewModel: EventLoggerViewModel) {
is Lce.Error -> { is Lce.Error -> {
// TODO // TODO
} }
is Lce.Loading -> { is Lce.Loading -> {
// TODO // TODO
} }
@ -69,7 +74,7 @@ private fun LogKeysList(keys: List<String>, onSelected: (String) -> Unit) {
} }
@Composable @Composable
private fun Events(selectedPageContent: SelectedState, onExit: () -> Unit, onSelectTag: (String?) -> Unit) { private fun Events(selectedPageContent: SelectedState, onExit: () -> Unit, onSelectTag: (String?) -> Unit, onRetry: () -> Unit) {
BackHandler(onBack = onExit) BackHandler(onBack = onExit)
when (val content = selectedPageContent.content) { when (val content = selectedPageContent.content) {
is Lce.Content -> { is Lce.Content -> {
@ -112,9 +117,8 @@ private fun Events(selectedPageContent: SelectedState, onExit: () -> Unit, onSel
} }
} }
} }
is Lce.Error -> TODO()
is Lce.Loading -> { is Lce.Error -> GenericError(cause = content.cause, action = onRetry)
// TODO is Lce.Loading -> CenteredLoading()
}
} }
} }

View File

@ -184,6 +184,7 @@ sealed interface ImportResult {
object NoKeysFound : Type object NoKeysFound : Type
object UnexpectedDecryptionOutput : Type object UnexpectedDecryptionOutput : Type
object UnableToOpenFile : Type object UnableToOpenFile : Type
object InvalidFile : Type
} }
} }

View File

@ -86,21 +86,27 @@ class RoomKeyImporter(
val line = it.joinToString(separator = "").replace("\n", "") val line = it.joinToString(separator = "").replace("\n", "")
val toByteArray = base64.decode(line) val toByteArray = base64.decode(line)
if (index == 0) { if (index == 0) {
decryptCipher.initialize(toByteArray, password) toByteArray.ensureHasCipherPayloadOrThrow()
toByteArray val initializer = toByteArray.copyOfRange(0, 37)
.copyOfRange(37, toByteArray.size) decryptCipher.initialize(initializer, password)
.decrypt(decryptCipher) val content = toByteArray.copyOfRange(37, toByteArray.size)
.also { content.decrypt(decryptCipher).also {
if (!it.startsWith("[{")) { if (!it.startsWith("[{")) {
throw ImportException(ImportResult.Error.Type.UnexpectedDecryptionOutput) throw ImportException(ImportResult.Error.Type.UnexpectedDecryptionOutput)
}
} }
}
} else { } else {
toByteArray.decrypt(decryptCipher) toByteArray.decrypt(decryptCipher)
} }
} }
} }
private fun ByteArray.ensureHasCipherPayloadOrThrow() {
if (this.size < 37) {
throw ImportException(ImportResult.Error.Type.InvalidFile)
}
}
private fun Cipher.initialize(payload: ByteArray, passphrase: String) { private fun Cipher.initialize(payload: ByteArray, passphrase: String) {
val salt = payload.copyOfRange(1, 1 + 16) val salt = payload.copyOfRange(1, 1 + 16)
val iv = payload.copyOfRange(17, 17 + 16) val iv = payload.copyOfRange(17, 17 + 16)
@ -176,6 +182,7 @@ private class JsonAccumulator {
jsonSegment = withLatest jsonSegment = withLatest
null null
} }
else -> { else -> {
val string = withLatest.substring(objectRange) val string = withLatest.substring(objectRange)
importJson.decodeFromString(ElementMegolmExportObject.serializer(), string).also { importJson.decodeFromString(ElementMegolmExportObject.serializer(), string).also {
@ -200,6 +207,7 @@ private class JsonAccumulator {
} }
opens++ opens++
} }
c == '}' -> { c == '}' -> {
opens-- opens--
if (opens == 0) { if (opens == 0) {