refactor: config

- add config to logs
- sensitive data filter
This commit is contained in:
rhunk
2024-06-01 18:13:46 +02:00
parent 2429ecd45a
commit c9114e7b61
14 changed files with 100 additions and 44 deletions

View File

@@ -191,7 +191,21 @@ class LogManager(
}
fun exportLogsToZip(outputStream: OutputStream) {
val zipOutputStream = ZipOutputStream(outputStream)
val zipOutputStream = ZipOutputStream(outputStream).apply {
setMethod(ZipOutputStream.DEFLATED)
}
// add device info to zip
zipOutputStream.putNextEntry(ZipEntry("device_info.json"))
val gson = GsonBuilder().setPrettyPrinting().create()
zipOutputStream.write(gson.toJson(remoteSideContext.installationSummary).toByteArray())
zipOutputStream.closeEntry()
// add config
zipOutputStream.putNextEntry(ZipEntry("config.json"))
zipOutputStream.write(remoteSideContext.config.exportToString(exportSensitiveData = false).toByteArray())
zipOutputStream.closeEntry()
//add logFolder to zip
logFolder.walk().forEach {
if (it.isFile) {
@@ -201,12 +215,6 @@ class LogManager(
}
}
//add device info to zip
zipOutputStream.putNextEntry(ZipEntry("device_info.json"))
val gson = GsonBuilder().setPrettyPrinting().create()
zipOutputStream.write(gson.toJson(remoteSideContext.installationSummary).toByteArray())
zipOutputStream.closeEntry()
zipOutputStream.close()
}

View File

@@ -94,6 +94,12 @@ class Routes(
routes.navController.navigate(replaceArguments(routeInfo.id, HashMap<String, String>().apply { args() }))
}
fun navigateReload() {
routes.navController.navigate(routeInfo.id) {
popUpTo(routeInfo.id) { inclusive = true }
}
}
fun navigateReset(args: MutableMap<String, String>.() -> Unit = {}) {
routes.navController.navigate(replaceArguments(routeInfo.id, HashMap<String, String>().apply { args() })) {
popUpTo(routes.navController.graph.findStartDestination().id) {

View File

@@ -529,6 +529,7 @@ class FeaturesRoot : Routes.Route() {
var showExportDropdownMenu by remember { mutableStateOf(false) }
var showResetConfirmationDialog by remember { mutableStateOf(false) }
var showExportDialog by remember { mutableStateOf(false) }
if (showResetConfirmationDialog) {
AlertDialog(
@@ -558,19 +559,50 @@ class FeaturesRoot : Routes.Route() {
)
}
val actions = remember {
mapOf(
translation["export_option"] to {
activityLauncher {
saveFile("config.json", "application/json") { uri ->
if (showExportDialog) {
fun exportConfig(
exportSensitiveData: Boolean
) {
showExportDialog = false
activityLauncher {
saveFile("config.json", "application/json") { uri ->
runCatching {
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
context.config.writeConfig()
context.config.exportToString().byteInputStream().copyTo(it)
context.config.exportToString(exportSensitiveData).byteInputStream().copyTo(it)
context.shortToast(translation["config_export_success_toast"])
}
}.onFailure {
context.longToast(translation.format("config_export_failure_toast", "error" to it.message.toString()))
}
}
}
}
AlertDialog(
title = { Text(text = context.translation["manager.dialogs.export_config.title"]) },
text = { Text(text = context.translation["manager.dialogs.export_config.content"]) },
onDismissRequest = { showExportDialog = false },
confirmButton = {
Button(
onClick = { exportConfig(true) }
) {
Text(text = context.translation["button.positive"])
}
},
dismissButton = {
Button(
onClick = { exportConfig(false) }
) {
Text(text = context.translation["button.negative"])
}
}
)
}
val actions = remember {
mapOf(
translation["export_option"] to { showExportDialog = true },
translation["import_option"] to {
activityLauncher {
openFile("application/json") { uri ->
@@ -582,6 +614,9 @@ class FeaturesRoot : Routes.Route() {
return@use
}
context.shortToast(translation["config_import_success_toast"])
context.coroutineScope.launch(Dispatchers.Main) {
navigateReload()
}
}
}
}

View File

@@ -68,9 +68,7 @@ class HomeLogs : Routes.Route() {
context.coroutineScope.launch {
context.log.clearLogs()
}
routes.navController.navigate(routeInfo.id) {
popUpTo(routeInfo.id) { inclusive = true }
}
navigateReload()
showDropDown = false
}, text = {
Text(translation["clear_logs_button"])

View File

@@ -82,6 +82,7 @@
"config_export_success_toast": "Config exported successfully",
"config_import_success_toast": "Config imported successfully",
"config_import_failure_toast": "Failed to import config {error}",
"config_export_failure_toast": "Failed to export config {error}",
"saved_config_snackbar": "Config saved"
},
"social": {
@@ -159,6 +160,10 @@
"content": "Are you sure you want to reset the config?",
"success_toast": "Config reset successfully"
},
"export_config": {
"title": "Export Sensitive Data?",
"content": "Do you want to export the config with sensitive data? (Such as location coordinates, etc.)"
},
"messaging_action": {
"title": "Choose content types to process",
"select_all_button": "Select All"

View File

@@ -74,10 +74,11 @@ open class ConfigContainer(
params: ConfigParamsBuilder = {}
) = registerProperty(key, DataProcessors.INT_COLOR, PropertyValue(defaultValue), params)
fun toJson(): JsonObject {
fun toJson(exportSensitiveData: Boolean = true): JsonObject {
val json = JsonObject()
properties.forEach { (propertyKey, propertyValue) ->
val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataType.serializeAny(it) }
if (!exportSensitiveData && propertyKey.params.flags.contains(ConfigFlag.SENSITIVE)) return@forEach
val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataType.serializeAny(it, exportSensitiveData) }
json.add(propertyKey.name, serializedValue)
}
return json

View File

@@ -29,7 +29,8 @@ enum class ConfigFlag {
USER_IMPORT,
NO_DISABLE_KEY,
REQUIRE_RESTART,
REQUIRE_CLEAN_CACHE;
REQUIRE_CLEAN_CACHE,
SENSITIVE;
val id = 1 shl ordinal
}

View File

@@ -22,17 +22,17 @@ object DataProcessors {
data class PropertyDataProcessor<T>
internal constructor(
val type: Type,
private val serialize: (T) -> JsonElement,
private val serialize: (T, exportSensitiveData: Boolean) -> JsonElement,
private val deserialize: (JsonElement) -> T
) {
@Suppress("UNCHECKED_CAST")
fun serializeAny(value: Any) = serialize(value as T)
fun serializeAny(value: Any, exportSensitiveData: Boolean) = serialize(value as T, exportSensitiveData)
fun deserializeAny(value: JsonElement) = deserialize(value)
}
val STRING = PropertyDataProcessor(
type = Type.STRING,
serialize = {
serialize = { it, _ ->
if (it != null) JsonPrimitive(it)
else JsonNull.INSTANCE
},
@@ -44,7 +44,7 @@ object DataProcessors {
val BOOLEAN = PropertyDataProcessor(
type = Type.BOOLEAN,
serialize = {
serialize = { it, _ ->
if (it) JsonPrimitive(true)
else JsonPrimitive(false)
},
@@ -53,19 +53,19 @@ object DataProcessors {
val INTEGER = PropertyDataProcessor(
type = Type.INTEGER,
serialize = { JsonPrimitive(it) },
serialize = { it, _ -> JsonPrimitive(it) },
deserialize = { it.asInt },
)
val FLOAT = PropertyDataProcessor(
type = Type.FLOAT,
serialize = { JsonPrimitive(it) },
serialize = { it, _ -> JsonPrimitive(it) },
deserialize = { it.asFloat },
)
val STRING_MULTIPLE_SELECTION = PropertyDataProcessor(
type = Type.STRING_MULTIPLE_SELECTION,
serialize = { JsonArray().apply { it.forEach { add(it) } } },
serialize = { it, _ -> JsonArray().apply { it.forEach { add(it) } } },
deserialize = { obj ->
obj.asJsonArray.map { it.asString }.toMutableList()
},
@@ -73,13 +73,13 @@ object DataProcessors {
val STRING_UNIQUE_SELECTION = PropertyDataProcessor(
type = Type.STRING_UNIQUE_SELECTION,
serialize = { JsonPrimitive(it) },
serialize = { it, _ -> JsonPrimitive(it) },
deserialize = { obj -> obj.takeIf { !it.isJsonNull }?.asString?.takeIf { it != "false" && it != "true" } }
)
val MAP_COORDINATES = PropertyDataProcessor(
type = Type.MAP_COORDINATES,
serialize = {
serialize = { it, _ ->
JsonObject().apply {
addProperty("lat", it.first.takeIf { it in -90.0..90.0 } ?: 0.0)
addProperty("lng", it.second.takeIf { it in -180.0..180.0 } ?: 0.0)
@@ -94,7 +94,7 @@ object DataProcessors {
val INT_COLOR = PropertyDataProcessor(
type = Type.INT_COLOR,
serialize = {
serialize = { it, _ ->
it?.let { JsonPrimitive(it) } ?: JsonNull.INSTANCE
},
deserialize = { if (it.isJsonNull) null else it.asString.toIntOrNull() },
@@ -102,17 +102,17 @@ object DataProcessors {
fun <T : ConfigContainer> container(container: T) = PropertyDataProcessor(
type = Type.CONTAINER,
serialize = {
serialize = { it, exportSensitiveData ->
JsonObject().apply {
addProperty("state", it.globalState)
add("properties", it.toJson())
add("properties", it.toJson(exportSensitiveData))
}
},
deserialize = { obj ->
val jsonObject = obj.asJsonObject
container.apply {
globalState = jsonObject["state"].takeIf { !it.isJsonNull }?.asBoolean
fromJson(jsonObject["properties"].asJsonObject)
globalState = jsonObject["state"]?.takeIf { !it.isJsonNull }?.asBoolean
jsonObject["properties"]?.asJsonObject?.let { fromJson(it) }
}
},
)

View File

@@ -54,10 +54,12 @@ class ModConfig(
root.fromJson(configObject)
}
fun exportToString(): String {
val configObject = root.toJson()
fun exportToString(
exportSensitiveData: Boolean = true
): String {
val configObject = root.toJson(exportSensitiveData)
configObject.addProperty("_locale", locale)
return configObject.toString()
return gson.toJson(configObject)
}
fun reset() {

View File

@@ -17,7 +17,7 @@ class DownloaderConfig : ConfigContainer() {
val customAudioCodec = string("custom_audio_codec") { addFlags(ConfigFlag.NO_TRANSLATE) }
}
val saveFolder = string("save_folder") { addFlags(ConfigFlag.FOLDER); requireRestart() }
val saveFolder = string("save_folder") { addFlags(ConfigFlag.FOLDER, ConfigFlag.SENSITIVE); requireRestart() }
val autoDownloadSources = multiple("auto_download_sources",
"friend_snaps",
"friend_stories",

View File

@@ -19,9 +19,9 @@ class Global : ConfigContainer() {
)
}
inner class BetterLocation : ConfigContainer(hasGlobalState = true) {
inner class BetterLocationConfig : ConfigContainer(hasGlobalState = true) {
val spoofLocation = boolean("spoof_location") { requireRestart() }
val coordinates = mapCoordinates("coordinates", 0.0 to 0.0) // lat, long
val coordinates = mapCoordinates("coordinates", 0.0 to 0.0) { addFlags(ConfigFlag.SENSITIVE) } // lat, long
val walkRadius = string("walk_radius") { requireRestart(); inputCheck = { it.toDoubleOrNull()?.isFinite() == true && it.toDouble() >= 0.0 } }
val alwaysUpdateLocation = boolean("always_update_location") { requireRestart() }
val suspendLocationUpdates = boolean("suspend_location_updates") { requireRestart() }
@@ -35,7 +35,7 @@ class Global : ConfigContainer() {
val customUploadImageFormat = unique("custom_image_upload_format", "jpeg", "png", "webp") { requireRestart(); addFlags(ConfigFlag.NO_TRANSLATE) }
}
val betterLocation = container("better_location", BetterLocation())
val betterLocation = container("better_location", BetterLocationConfig())
val snapchatPlus = boolean("snapchat_plus") { requireRestart() }
val mediaUploadQualityConfig = container("media_upload_quality", MediaUploadQualityConfig())
val disableConfirmationDialogs = multiple("disable_confirmation_dialogs", "erase_message", "remove_friend", "block_friend", "ignore_friend", "hide_friend", "hide_conversation", "clear_conversation") { requireRestart() }

View File

@@ -5,7 +5,7 @@ import me.rhunk.snapenhance.common.config.ConfigFlag
class Scripting : ConfigContainer() {
val developerMode = boolean("developer_mode", false) { requireRestart() }
val moduleFolder = string("module_folder", "modules") { addFlags(ConfigFlag.FOLDER); requireRestart() }
val moduleFolder = string("module_folder", "modules") { addFlags(ConfigFlag.FOLDER, ConfigFlag.SENSITIVE); requireRestart() }
val autoReload = unique("auto_reload", "snapchat_only", "all")
val integratedUI = boolean("integrated_ui", false) { requireRestart() }
val disableLogAnonymization = boolean("disable_log_anonymization", false) { requireRestart() }

View File

@@ -1,11 +1,12 @@
package me.rhunk.snapenhance.common.config.impl
import me.rhunk.snapenhance.common.config.ConfigContainer
import me.rhunk.snapenhance.common.config.ConfigFlag
class Spoof : ConfigContainer(hasGlobalState = true) {
val overridePlayStoreInstallerPackageName = boolean("play_store_installer_package_name")
val fingerprint = string("fingerprint")
val androidId = string("android_id")
val fingerprint = string("fingerprint") { addFlags(ConfigFlag.SENSITIVE) }
val androidId = string("android_id") { addFlags(ConfigFlag.SENSITIVE) }
val removeVpnTransportFlag = boolean("remove_vpn_transport_flag")
val removeMockLocationFlag = boolean("remove_mock_location_flag")
val randomizePersistentDeviceToken = boolean("randomize_persistent_device_token")

View File

@@ -89,7 +89,6 @@ class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams
editor.apply {
// SCVSLocationUpdate
edit(1) {
context.log.verbose("SCVSLocationUpdate ${this@apply}")
if (config.spoofLocation.get()) {
remove(1)
remove(2)