Skip to content

Commit

Permalink
Merge branch 'release/candidate' into refactor/remove-user-scoped-sha…
Browse files Browse the repository at this point in the history
…red-pref
  • Loading branch information
MohamadJaara authored Nov 16, 2023
2 parents 24b1a0a + 3b94f7e commit 196ccc4
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 123 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ sqldelight = "2.0.0"
sqlcipher-android = "4.5.5"
pbandk = "0.14.2"
turbine = "1.0.0"
avs = "9.5.4"
avs = "9.5.5"
jna = "5.13.0"
core-crypto = "1.0.0-rc.17"
core-crypto-multiplatform = "0.6.0-rc.3-multiplatform-pre1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@ import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.asset.KaliumFileSystem
import com.wire.kalium.logic.functional.Either
import okio.BufferedSource
import okio.Source
import okio.Sink
import okio.Path
import okio.Sink
import okio.Source

actual fun createCompressedFile(files: List<Pair<Source, String>>, outputSink: Sink): Either<CoreFailure, Long> =
TODO("Implement own iOS compression method")

actual fun extractCompressedFile(inputSource: Source, outputRootPath: Path, fileSystem: KaliumFileSystem): Either<CoreFailure, Long> =
actual fun extractCompressedFile(
inputSource: Source,
outputRootPath: Path,
param: ExtractFilesParam,
fileSystem: KaliumFileSystem
): Either<CoreFailure, Long> =
TODO("Implement own iOS compression method")

actual fun checkIfCompressedFileContainsFileTypes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,45 @@ private fun addToCompressedFile(zipOutputStream: ZipOutputStream, fileSource: So
}

@Suppress("TooGenericExceptionCaught", "NestedBlockDepth")
actual fun extractCompressedFile(inputSource: Source, outputRootPath: Path, fileSystem: KaliumFileSystem): Either<CoreFailure, Long> = try {
actual fun extractCompressedFile(
inputSource: Source,
outputRootPath: Path,
param: ExtractFilesParam,
fileSystem: KaliumFileSystem
): Either<CoreFailure, Long> = try {
var totalExtractedFilesSize = 0L
ZipInputStream(inputSource.buffer().inputStream()).use { zipInputStream ->
var entry: ZipEntry? = zipInputStream.nextEntry
while (entry != null) {
readCompressedEntry(zipInputStream, outputRootPath, fileSystem, entry).let {
totalExtractedFilesSize += it.first
entry = it.second
totalExtractedFilesSize += when (param) {
is ExtractFilesParam.All -> readCompressedEntry(zipInputStream, outputRootPath, fileSystem, entry)
is ExtractFilesParam.Only -> readAndExtractIfMatch(zipInputStream, outputRootPath, fileSystem, entry, param.files)
}
zipInputStream.closeEntry()
entry = zipInputStream.nextEntry
}
}
Either.Right(totalExtractedFilesSize)
} catch (e: Exception) {
Either.Left(StorageFailure.Generic(RuntimeException("There was an error trying to extract the provided compressed file", e)))
}

private fun readAndExtractIfMatch(
zipInputStream: ZipInputStream,
outputRootPath: Path,
fileSystem: KaliumFileSystem,
entry: ZipEntry,
fileNames: Set<String>
): Long {
return entry.name.let {
if (fileNames.contains(it)) {
readCompressedEntry(zipInputStream, outputRootPath, fileSystem, entry)
} else {
0L
}
}
}

@Suppress("TooGenericExceptionCaught", "NestedBlockDepth")
actual fun checkIfCompressedFileContainsFileTypes(
compressedFilePath: Path,
Expand Down Expand Up @@ -121,11 +144,7 @@ private fun readCompressedEntry(
outputRootPath: Path,
fileSystem: KaliumFileSystem,
entry: ZipEntry
): Pair<Long, ZipEntry?> {
if (isInvalidEntryPathDestination(entry.name)) {
throw RuntimeException("The provided zip file is invalid or has invalid data")
}

): Long {
var totalExtractedFilesSize = 0L
var byteCount: Int
val entryPathName = "$outputRootPath/${entry.name}"
Expand All @@ -137,8 +156,7 @@ private fun readCompressedEntry(
}
output.write(zipInputStream.readBytes())
}
zipInputStream.closeEntry()
return totalExtractedFilesSize to zipInputStream.nextEntry
return totalExtractedFilesSize
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,6 @@ sealed interface CoreFailure {
*/
data object SyncEventOrClientNotFound : FeatureFailure()

/**
* The desired event was not found when fetching pending events.
* This can happen when this client is old and the server have new event types
* that the client does not know how to handle.
* the event is skipped and the sync continues
*/
data object FeatureNotImplemented : FeatureFailure()
/**
* No common Protocol found in order to establish a conversation between parties.
* Could be, for example, that the desired user only supports Proteus, but we only support MLS.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ package com.wire.kalium.logic.feature.backup
object BackupConstants {
const val BACKUP_FILE_NAME_PREFIX = "WBX"
const val BACKUP_ENCRYPTED_FILE_NAME = "user-backup.cc20"

// BACKUP_METADATA_FILE_NAME and BACKUP_USER_DB_NAME must not be changed
// if there is a need to change them, please create a new file names and add it to the list of acceptedFileNames()
const val BACKUP_USER_DB_NAME = "user-backup-database.db"
const val BACKUP_METADATA_FILE_NAME = "export.json"
const val BACKUP_ENCRYPTED_EXTENSION = "cc20"
Expand All @@ -30,6 +33,16 @@ object BackupConstants {
const val BACKUP_WEB_EVENTS_FILE_NAME = "events.json"
const val BACKUP_WEB_CONVERSATIONS_FILE_NAME = "conversations.json"

/**
* list of accepted file names for the backup file
* this is used when extracting data from the zip file
*/
fun acceptedFileNames() = setOf(
BACKUP_USER_DB_NAME,
BACKUP_METADATA_FILE_NAME,
BACKUP_ENCRYPTED_FILE_NAME
)

fun createBackupFileName(userHandle: String?, timestampIso: String) = // file names cannot have special characters
"$BACKUP_FILE_NAME_PREFIX-$userHandle-${timestampIso.replace(":", "-")}.zip"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ import com.wire.kalium.cryptography.backup.BackupHeader.HeaderDecodingErrors.INV
import com.wire.kalium.cryptography.backup.Passphrase
import com.wire.kalium.cryptography.utils.ChaCha20Decryptor.decryptBackupFile
import com.wire.kalium.logic.data.asset.KaliumFileSystem
import com.wire.kalium.logic.data.id.CurrentClientIdProvider
import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.di.MapperProvider
import com.wire.kalium.logic.data.id.CurrentClientIdProvider
import com.wire.kalium.logic.feature.backup.BackupConstants.BACKUP_ENCRYPTED_EXTENSION
import com.wire.kalium.logic.feature.backup.BackupConstants.acceptedFileNames
import com.wire.kalium.logic.feature.backup.BackupConstants.createBackupFileName
import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.BackupIOFailure
import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.DecryptionFailure
Expand All @@ -43,6 +44,7 @@ import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.functional.mapLeft
import com.wire.kalium.logic.kaliumLogger
import com.wire.kalium.logic.util.ExtractFilesParam
import com.wire.kalium.logic.util.extractCompressedFile
import com.wire.kalium.logic.wrapStorageRequest
import com.wire.kalium.network.tools.KtxSerializer
Expand Down Expand Up @@ -192,23 +194,19 @@ internal class RestoreBackupUseCaseImpl(
when (decodingError) {
INVALID_USER_ID -> InvalidUserId
INVALID_VERSION -> IncompatibleBackup("The provided backup version is lower than the minimum supported version")
INVALID_FORMAT -> IncompatibleBackup("The provided backup format is not supported")
INVALID_FORMAT -> IncompatibleBackup("mappedDecodingError: The provided backup format is not supported")
}

private suspend fun checkIsValidEncryption(extractedBackupPath: Path): Either<Failure, Path> =
with(kaliumFileSystem) {
val encryptedFilePath = listDirectories(extractedBackupPath).firstOrNull {
it.name.substringAfterLast('.', "") == BACKUP_ENCRYPTED_EXTENSION
}
return if (encryptedFilePath == null) {
Either.Left(Failure(DecryptionFailure("No encrypted backup file found")))
} else {
Either.Right(encryptedFilePath)
}
listDirectories(extractedBackupPath)
.firstOrNull { it.name.endsWith(".$BACKUP_ENCRYPTED_EXTENSION") }?.let {
Either.Right(it)
} ?: Either.Left(Failure(DecryptionFailure("No encrypted backup file found")))
}

private fun extractFiles(inputSource: Source, extractedBackupRootPath: Path) =
extractCompressedFile(inputSource, extractedBackupRootPath, kaliumFileSystem)
extractCompressedFile(inputSource, extractedBackupRootPath, ExtractFilesParam.Only(acceptedFileNames()), kaliumFileSystem)

private suspend fun getDbPathAndImport(
extractedBackupRootPath: Path,
Expand All @@ -230,14 +228,15 @@ internal class RestoreBackupUseCaseImpl(
private suspend fun backupMetadata(extractedBackupPath: Path): Either<Failure, BackupMetadata> =
kaliumFileSystem.listDirectories(extractedBackupPath)
.firstOrNull { it.name == BackupConstants.BACKUP_METADATA_FILE_NAME }
?.let { metadataFile ->
.let { it ?: return Either.Left(Failure(IncompatibleBackup("backupMetadata: No metadata file found"))) }
.let { metadataFile ->
try {
kaliumFileSystem.source(metadataFile).buffer()
.use { Either.Right(KtxSerializer.json.decodeFromString(it.readUtf8())) }
} catch (e: SerializationException) {
Either.Left(Failure(IncompatibleBackup(e.toString())))
}
} ?: Either.Left(Failure(IncompatibleBackup("The provided backup format is not supported")))
}

private fun isValidBackupAuthor(metadata: BackupMetadata): Either<Failure, BackupMetadata> =
if (metadata.userId == userId.toString() || metadata.userId == userId.value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ internal class RestoreWebBackupUseCaseImpl(
if (metadata.version == "19") {
importWebBackup(backupRootPath, this)
} else {
Either.Left(IncompatibleBackup("The provided backup format is not supported"))
Either.Left(IncompatibleBackup("invoke: The provided backup format is not supported"))
}.fold({ RestoreBackupResult.Failure(it) }, { RestoreBackupResult.Success })
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import com.wire.kalium.logic.data.event.EventLoggingStatus
import com.wire.kalium.logic.data.event.EventRepository
import com.wire.kalium.logic.data.event.logEventProcessing
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMapLeft
import com.wire.kalium.logic.functional.onSuccess
import com.wire.kalium.logic.kaliumLogger
import com.wire.kalium.logic.sync.receiver.ConversationEventReceiver
Expand Down Expand Up @@ -75,35 +74,29 @@ internal class EventProcessorImpl(

override var disableEventProcessing: Boolean = false

override suspend fun processEvent(event: Event): Either<CoreFailure, Unit> =
override suspend fun processEvent(event: Event): Either<CoreFailure, Unit> {
if (disableEventProcessing) {
logger.w("Skipping processing of $event due to debug option")
Either.Right(Unit)
} else {
when (event) {
is Event.Conversation -> conversationEventReceiver.onEvent(event)
is Event.User -> userEventReceiver.onEvent(event)
is Event.FeatureConfig -> featureConfigEventReceiver.onEvent(event)
is Event.Unknown -> {
kaliumLogger
.logEventProcessing(
EventLoggingStatus.SKIPPED,
event
)
// Skipping event = success
Either.Right(Unit)
}
return Either.Right(Unit)
}

is Event.Team -> teamEventReceiver.onEvent(event)
is Event.UserProperty -> userPropertiesEventReceiver.onEvent(event)
is Event.Federation -> federationEventReceiver.onEvent(event)
}
}.flatMapLeft {
if (it is CoreFailure.FeatureNotImplemented) {
return when (event) {
is Event.Conversation -> conversationEventReceiver.onEvent(event)
is Event.User -> userEventReceiver.onEvent(event)
is Event.FeatureConfig -> featureConfigEventReceiver.onEvent(event)
is Event.Unknown -> {
kaliumLogger
.logEventProcessing(
EventLoggingStatus.SKIPPED,
event
)
// Skipping event = success
Either.Right(Unit)
} else {
Either.Left(it)
}

is Event.Team -> teamEventReceiver.onEvent(event)
is Event.UserProperty -> userPropertiesEventReceiver.onEvent(event)
is Event.Federation -> federationEventReceiver.onEvent(event)
}.onSuccess {
val logMap = mapOf<String, Any>(
"event" to event.toLogMap()
Expand All @@ -115,6 +108,7 @@ internal class EventProcessorImpl(
logger.i("Skipping update of lastProcessedEventId: ${logMap.toJsonElement()}")
}
}
}

private fun Event.shouldUpdateLastProcessedEventId(): Boolean = !transient
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,33 +60,33 @@ internal class FeatureConfigEventReceiverImpl internal constructor(
)
}
.onFailure {
if (it is CoreFailure.FeatureNotImplemented) {
kaliumLogger.logEventProcessing(
EventLoggingStatus.SKIPPED,
event,
Pair("info", "Ignoring unknown feature config update")
)
} else {
kaliumLogger.logEventProcessing(
EventLoggingStatus.FAILURE,
event,
Pair("error", it)
)
}
}
kaliumLogger.logEventProcessing(
EventLoggingStatus.FAILURE,
event,
Pair("error", it)
)
}

@Suppress("LongMethod", "ComplexMethod")
private suspend fun handleFeatureConfigEvent(event: Event.FeatureConfig): Either<CoreFailure, Unit> =
when (event) {
is Event.FeatureConfig.FileSharingUpdated -> fileSharingConfigHandler.handle(event.model)
is Event.FeatureConfig.MLSUpdated -> mlsConfigHandler.handle(event.model, duringSlowSync = false)
is Event.FeatureConfig.MLSMigrationUpdated -> mlsMigrationConfigHandler.handle(event.model, duringSlowSync = false)
is Event.FeatureConfig.ClassifiedDomainsUpdated -> classifiedDomainsConfigHandler.handle(event.model)
is Event.FeatureConfig.ConferenceCallingUpdated -> conferenceCallingConfigHandler.handle(event.model)
is Event.FeatureConfig.GuestRoomLinkUpdated -> guestRoomConfigHandler.handle(event.model)
is Event.FeatureConfig.SelfDeletingMessagesConfig -> selfDeletingMessagesConfigHandler.handle(event.model)
is Event.FeatureConfig.MLSE2EIUpdated -> e2EIConfigHandler.handle(event.model)
is Event.FeatureConfig.AppLockUpdated -> appLockConfigHandler.handle(event.model)
is Event.FeatureConfig.UnknownFeatureUpdated -> {
kaliumLogger.logEventProcessing(
EventLoggingStatus.SKIPPED,
event,
Pair("info", "Ignoring unknown feature config update")
)

@Suppress("LongMethod", "ComplexMethod")
private suspend fun handleFeatureConfigEvent(event: Event.FeatureConfig): Either<CoreFailure, Unit> =
when (event) {
is Event.FeatureConfig.FileSharingUpdated -> fileSharingConfigHandler.handle(event.model)
is Event.FeatureConfig.MLSUpdated -> mlsConfigHandler.handle(event.model, duringSlowSync = false)
is Event.FeatureConfig.MLSMigrationUpdated -> mlsMigrationConfigHandler.handle(event.model, duringSlowSync = false)
is Event.FeatureConfig.ClassifiedDomainsUpdated -> classifiedDomainsConfigHandler.handle(event.model)
is Event.FeatureConfig.ConferenceCallingUpdated -> conferenceCallingConfigHandler.handle(event.model)
is Event.FeatureConfig.GuestRoomLinkUpdated -> guestRoomConfigHandler.handle(event.model)
is Event.FeatureConfig.SelfDeletingMessagesConfig -> selfDeletingMessagesConfigHandler.handle(event.model)
is Event.FeatureConfig.MLSE2EIUpdated -> e2EIConfigHandler.handle(event.model)
is Event.FeatureConfig.AppLockUpdated -> appLockConfigHandler.handle(event.model)
is Event.FeatureConfig.UnknownFeatureUpdated -> Either.Left(CoreFailure.FeatureNotImplemented)
Either.Right(Unit)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,24 @@ import okio.Sink
import okio.Source

expect fun createCompressedFile(files: List<Pair<Source, String>>, outputSink: Sink): Either<CoreFailure, Long>
expect fun extractCompressedFile(inputSource: Source, outputRootPath: Path, fileSystem: KaliumFileSystem): Either<CoreFailure, Long>
expect fun extractCompressedFile(
inputSource: Source,
outputRootPath: Path,
param: ExtractFilesParam,
fileSystem: KaliumFileSystem
): Either<CoreFailure, Long>

expect fun checkIfCompressedFileContainsFileTypes(
compressedFilePath: Path,
fileSystem: KaliumFileSystem,
expectedFileExtensions: List<String>
): Either<CoreFailure, Map<String, Boolean>>

sealed interface ExtractFilesParam {
data object All : ExtractFilesParam
data class Only(val files: Set<String>) : ExtractFilesParam {
constructor(vararg files: String) : this(files.toSet())
}
}

expect inline fun <reified T> decodeBufferSequence(bufferedSource: BufferedSource): Sequence<T>
Loading

0 comments on commit 196ccc4

Please sign in to comment.