From 6731edde87ff6536a18e1e435efc81e31c27a642 Mon Sep 17 00:00:00 2001 From: "Cuong M. Tran" Date: Fri, 19 Jan 2024 18:06:12 +0700 Subject: [PATCH] Add OFFICIAL source & mine signature --- .../interactor/CreateExtensionRepo.kt | 5 +++-- .../browse/ExtensionDetailsScreen.kt | 5 +++++ .../presentation/browse/ExtensionsScreen.kt | 1 + .../tachiyomi/extension/api/ExtensionApi.kt | 9 +++++++- .../tachiyomi/extension/model/Extension.kt | 1 + .../extension/util/ExtensionLoader.kt | 21 ++++++++++++++++++- .../commonMain/resources/MR/base/strings.xml | 2 ++ 7 files changed, 40 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt index 66b949fc9..c8912e85b 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt @@ -6,8 +6,8 @@ import tachiyomi.core.preference.plusAssign class CreateExtensionRepo(private val preferences: SourcePreferences) { fun await(name: String): Result { - // Do not allow invalid formats - if (!name.matches(repoRegex)) { + // Do not allow invalid formats & avoid adding duplicating official repo + if (!name.matches(repoRegex) || name.startsWith(OFFICIAL_REPO_BASE_URL)) { return Result.InvalidUrl } @@ -22,4 +22,5 @@ class CreateExtensionRepo(private val preferences: SourcePreferences) { } } +const val OFFICIAL_REPO_BASE_URL = "https://raw.githubusercontent.com/cuong-tran/tachiyomi-extensions/repo" private val repoRegex = """^https://.*/index\.min\.json$""".toRegex() diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index d140a1621..bd7774c7c 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -170,6 +170,11 @@ private fun ExtensionDetails( } } // SY <-- + if (extension.isUnofficial) { + item { + WarningBanner(MR.strings.unofficial_extension_message) + } + } if (extension.isObsolete) { item { WarningBanner(MR.strings.obsolete_extension_message) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index d88399eb9..394a661f3 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -354,6 +354,7 @@ private fun ExtensionItemContent( val warning = when { extension is Extension.Untrusted -> MR.strings.ext_untrusted + extension is Extension.Installed && extension.isUnofficial -> MR.strings.ext_unofficial extension is Extension.Installed && extension.isObsolete -> MR.strings.ext_obsolete // SY --> extension is Extension.Installed && extension.isRedundant -> SYMR.strings.ext_redundant diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt index 7c1a33ecf..e97b4597f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.extension.api import android.content.Context +import eu.kanade.domain.extension.interactor.OFFICIAL_REPO_BASE_URL import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.model.Extension @@ -36,7 +37,13 @@ internal class ExtensionApi { suspend fun findExtensions(): List { return withIOContext { - sourcePreferences.extensionRepos().get().flatMap { getExtensions(it) } + // Combine built-in OFFICIAL repo's extensions list with repo's list + val extensions = buildList { + addAll(getExtensions(OFFICIAL_REPO_BASE_URL)) + sourcePreferences.extensionRepos().get().map { addAll(getExtensions(it)) } + } + + extensions } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt index 2fa430ff0..1789f8887 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt @@ -27,6 +27,7 @@ sealed class Extension { val icon: Drawable?, val hasUpdate: Boolean = false, val isObsolete: Boolean = false, + val isUnofficial: Boolean = false, val isShared: Boolean, val repoUrl: String? = null, // SY --> diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index ef603f934..c90c6e493 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -59,6 +59,9 @@ internal object ExtensionLoader { PackageManager.GET_SIGNATURES or (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) PackageManager.GET_SIGNING_CERTIFICATES else 0) + // cuong-tran's key + private const val officialSignature = "cbec121aa82ebb02aaa73806992e0368a97d47b5451ed6524816d03084c45905" + private const val PRIVATE_EXTENSION_EXTENSION = "ext" private fun getPrivateExtensionDir(context: Context) = File(context.filesDir, "exts") @@ -252,7 +255,8 @@ internal object ExtensionLoader { if (signatures.isNullOrEmpty()) { logcat(LogPriority.WARN) { "Package $pkgName isn't signed" } return LoadResult.Error - } else if (!trustExtension.isTrusted(pkgInfo, signatures.last())) { + } else if (!isTrusted(pkgInfo, signatures.last())) { + // If not trusted then will load it as Untrusted extension to force use to Trust it val extension = Extension.Untrusted( extName, pkgName, @@ -320,6 +324,7 @@ internal object ExtensionLoader { isNsfw = isNsfw, sources = sources, pkgFactory = appInfo.metaData.getString(METADATA_SOURCE_FACTORY), + isUnofficial = !isOfficiallySigned(signatures), icon = appInfo.loadIcon(pkgManager), isShared = extensionInfo.isShared, ) @@ -379,6 +384,20 @@ internal object ExtensionLoader { ?.toList() } + private fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean { + if (officialSignature == signatureHash) { + return true + } + return trustExtension.isTrusted(pkgInfo, signatureHash) + } + + /** + * Showing UNOFFICIAL text on extension + */ + private fun isOfficiallySigned(signatures: List): Boolean { + return signatures.all { it == officialSignature } + } + /** * On Android 13+ the ApplicationInfo generated by getPackageArchiveInfo doesn't * have sourceDir which breaks assets loading (used for getting icon here). diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 18457330d..c67c085c1 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -311,12 +311,14 @@ Installing Installed Trust + Unofficial Untrusted Uninstall App info Untrusted extension Malicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension, you accept these risks. This extension is no longer available. It may not function properly and can cause issues with the app. Uninstalling it is recommended. + This extension is not from the official repo. Failed to fetch available extensions Version Language