mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-04-24 16:57:15 -04:00
[db] add archive repo adding to RepoAdder
This commit is contained in:
@@ -27,6 +27,7 @@ import org.fdroid.repo.RepoAdder
|
||||
import org.fdroid.repo.RepoUriGetter
|
||||
import java.io.File
|
||||
import java.net.Proxy
|
||||
import java.util.concurrent.CancellationException
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@@ -36,10 +37,9 @@ public class RepoManager @JvmOverloads constructor(
|
||||
private val db: FDroidDatabase,
|
||||
downloaderFactory: DownloaderFactory,
|
||||
httpManager: HttpManager,
|
||||
private val repoUriBuilder: RepoUriBuilder = defaultRepoUriBuilder,
|
||||
repoUriBuilder: RepoUriBuilder = defaultRepoUriBuilder,
|
||||
private val coroutineContext: CoroutineContext = Dispatchers.IO,
|
||||
) {
|
||||
|
||||
private val repositoryDao = db.getRepositoryDao() as RepositoryDaoInt
|
||||
private val appPrefsDao = db.getAppPrefsDao() as AppPrefsDaoInt
|
||||
private val tempFileProvider = TempFileProvider {
|
||||
@@ -197,6 +197,36 @@ public class RepoManager @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disabled the archive repo for the given [repository].
|
||||
*
|
||||
* Note that this can throw all kinds of exceptions,
|
||||
* especially when the given [repository] does not have a (working) archive repository.
|
||||
* You should catch those and update your UI accordingly.
|
||||
*/
|
||||
@WorkerThread
|
||||
public suspend fun setArchiveRepoEnabled(
|
||||
repository: Repository,
|
||||
enabled: Boolean,
|
||||
proxy: Proxy? = null,
|
||||
) {
|
||||
val cert = repository.certificate ?: error { "$repository has no cert" }
|
||||
val archiveRepoId = repositoryDao.getArchiveRepoId(cert)
|
||||
if (enabled) {
|
||||
if (archiveRepoId == null) {
|
||||
try {
|
||||
repoAdder.addArchiveRepo(repository, proxy)
|
||||
} catch (e: CancellationException) {
|
||||
if (e.message != "expected") throw e
|
||||
}
|
||||
} else {
|
||||
repositoryDao.setRepositoryEnabled(archiveRepoId, true)
|
||||
}
|
||||
} else if (archiveRepoId != null) {
|
||||
repositoryDao.setRepositoryEnabled(archiveRepoId, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given [uri] belongs to a swap repo.
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.UserManager
|
||||
import android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES
|
||||
import android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
@@ -13,8 +14,10 @@ import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.SerializationException
|
||||
import mu.KotlinLogging
|
||||
import org.fdroid.database.AppOverviewItem
|
||||
@@ -166,21 +169,7 @@ internal class RepoAdder(
|
||||
|
||||
// try fetching repo with v2 format first and fallback to v1
|
||||
try {
|
||||
try {
|
||||
val repo =
|
||||
getTempRepo(nUri.uri, IndexFormatVersion.TWO, nUri.username, nUri.password)
|
||||
val repoFetcher = RepoV2Fetcher(
|
||||
tempFileProvider, downloaderFactory, httpManager, repoUriBuilder, proxy
|
||||
)
|
||||
repoFetcher.fetchRepo(nUri.uri, repo, receiver, nUri.fingerprint)
|
||||
} catch (e: NotFoundException) {
|
||||
log.warn(e) { "Did not find v2 repo, trying v1 now." }
|
||||
// try to fetch v1 repo
|
||||
val repo =
|
||||
getTempRepo(nUri.uri, IndexFormatVersion.ONE, nUri.username, nUri.password)
|
||||
val repoFetcher = RepoV1Fetcher(tempFileProvider, downloaderFactory, repoUriBuilder)
|
||||
repoFetcher.fetchRepo(nUri.uri, repo, receiver, nUri.fingerprint)
|
||||
}
|
||||
fetchRepo(nUri.uri, nUri.fingerprint, proxy, nUri.username, nUri.password, receiver)
|
||||
} catch (e: SigningException) {
|
||||
log.error(e) { "Error verifying repo with given fingerprint." }
|
||||
addRepoState.value = AddRepoError(INVALID_FINGERPRINT, e)
|
||||
@@ -207,6 +196,31 @@ internal class RepoAdder(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchRepo(
|
||||
uri: Uri,
|
||||
fingerprint: String?,
|
||||
proxy: Proxy?,
|
||||
username: String?,
|
||||
password: String?,
|
||||
receiver: RepoPreviewReceiver,
|
||||
) {
|
||||
try {
|
||||
val repo =
|
||||
getTempRepo(uri, IndexFormatVersion.TWO, username, password)
|
||||
val repoFetcher = RepoV2Fetcher(
|
||||
tempFileProvider, downloaderFactory, httpManager, repoUriBuilder, proxy
|
||||
)
|
||||
repoFetcher.fetchRepo(uri, repo, receiver, fingerprint)
|
||||
} catch (e: NotFoundException) {
|
||||
log.warn(e) { "Did not find v2 repo, trying v1 now." }
|
||||
// try to fetch v1 repo
|
||||
val repo =
|
||||
getTempRepo(uri, IndexFormatVersion.ONE, username, password)
|
||||
val repoFetcher = RepoV1Fetcher(tempFileProvider, downloaderFactory, repoUriBuilder)
|
||||
repoFetcher.fetchRepo(uri, repo, receiver, fingerprint)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFetchResult(url: String, repo: Repository): FetchResult {
|
||||
val cert = repo.certificate ?: error("Certificate was null")
|
||||
val existingRepo = repositoryDao.getRepository(cert)
|
||||
@@ -293,6 +307,41 @@ internal class RepoAdder(
|
||||
fetchJob?.cancel()
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
internal suspend fun addArchiveRepo(repo: Repository, proxy: Proxy? = null) =
|
||||
withContext(coroutineContext) {
|
||||
if (repo.isArchiveRepo) error { "Repo ${repo.address} is already an archive repo." }
|
||||
|
||||
val address = repo.address.replace(Regex("repo/?$"), "archive")
|
||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
val receiver = object : RepoPreviewReceiver {
|
||||
override fun onRepoReceived(archiveRepo: Repository) {
|
||||
// reset the timestamp of the actual repo,
|
||||
// so a following repo update will pick this up
|
||||
val newRepo = NewRepository(
|
||||
name = archiveRepo.repository.name,
|
||||
icon = archiveRepo.repository.icon ?: emptyMap(),
|
||||
address = archiveRepo.address,
|
||||
formatVersion = archiveRepo.formatVersion,
|
||||
certificate = archiveRepo.certificate ?: error("Repo had no certificate"),
|
||||
username = archiveRepo.username,
|
||||
password = archiveRepo.password,
|
||||
)
|
||||
db.runInTransaction {
|
||||
val repoId = repositoryDao.insert(newRepo)
|
||||
repositoryDao.setWeight(repoId, repo.weight - 1)
|
||||
}
|
||||
cancel("expected") // no need to continue downloading the entire repo
|
||||
}
|
||||
|
||||
override fun onAppReceived(app: AppOverviewItem) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
val uri = Uri.parse(address)
|
||||
fetchRepo(uri, repo.fingerprint, proxy, repo.username, repo.password, receiver)
|
||||
}
|
||||
|
||||
private fun hasDisallowInstallUnknownSources(context: Context): Boolean {
|
||||
val userManager = getSystemService(context, UserManager::class.java)
|
||||
?: error("No UserManager available.")
|
||||
|
||||
Reference in New Issue
Block a user