diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt index 22b44c4d6..c964c81e2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CloudFlareActivity.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.browser.cloudflare import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.Menu import android.view.MenuItem import android.webkit.CookieManager import androidx.activity.result.contract.ActivityResultContract @@ -13,6 +14,7 @@ import androidx.core.view.isVisible import androidx.core.view.updatePadding import dagger.hilt.android.AndroidEntryPoint import okhttp3.Headers +import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.koitharu.kotatsu.R import org.koitharu.kotatsu.browser.WebViewBackPressedCallback @@ -38,7 +40,13 @@ class CloudFlareActivity : BaseActivity(), CloudFlareCal override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (!catchingWebViewUnavailability { setContentView(ActivityBrowserBinding.inflate(layoutInflater)) }) { + if (!catchingWebViewUnavailability { + setContentView( + ActivityBrowserBinding.inflate( + layoutInflater + ) + ) + }) { return } supportActionBar?.run { @@ -86,6 +94,11 @@ class CloudFlareActivity : BaseActivity(), CloudFlareCal viewBinding.webView.restoreState(savedInstanceState) } + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.opt_captcha, menu) + return super.onCreateOptionsMenu(menu) + } + override fun onWindowInsetsChanged(insets: Insets) { viewBinding.appbar.updatePadding( top = insets.top, @@ -104,6 +117,16 @@ class CloudFlareActivity : BaseActivity(), CloudFlareCal true } + R.id.action_retry -> { + viewBinding.webView.stopLoading() + val targetUrl = intent?.dataString?.toHttpUrlOrNull() + if (targetUrl != null) { + clearCfCookies(targetUrl) + viewBinding.webView.loadUrl(targetUrl.toString()) + } + true + } + else -> super.onOptionsItemSelected(item) } @@ -141,7 +164,15 @@ class CloudFlareActivity : BaseActivity(), CloudFlareCal override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) { setTitle(title) - supportActionBar?.subtitle = subtitle?.toString()?.toHttpUrlOrNull()?.topPrivateDomain() ?: subtitle + supportActionBar?.subtitle = + subtitle?.toString()?.toHttpUrlOrNull()?.topPrivateDomain() ?: subtitle + } + + private fun clearCfCookies(url: HttpUrl) { + cookieJar.removeCookies(url) { cookie -> + val name = cookie.name + name.startsWith("cf_") || name.startsWith("_cf") || name.startsWith("__cf") + } } class Contract : ActivityResultContract, TaggedActivityResult>() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/AndroidCookieJar.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/AndroidCookieJar.kt index 8a6dabc30..1352257d5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/AndroidCookieJar.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/AndroidCookieJar.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.network.cookies import android.webkit.CookieManager import androidx.annotation.WorkerThread +import androidx.core.util.Predicate import okhttp3.Cookie import okhttp3.HttpUrl import org.koitharu.kotatsu.core.util.ext.newBuilder @@ -31,13 +32,16 @@ class AndroidCookieJar : MutableCookieJar { } } - override fun removeCookies(url: HttpUrl) { + override fun removeCookies(url: HttpUrl, predicate: Predicate?) { val cookies = loadForRequest(url) if (cookies.isEmpty()) { return } val urlString = url.toString() for (c in cookies) { + if (predicate != null && !predicate.test(c)) { + continue + } val nc = c.newBuilder() .expiresAt(System.currentTimeMillis() - 100000) .build() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/MutableCookieJar.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/MutableCookieJar.kt index 01d81a8d2..a1f1b3ec5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/MutableCookieJar.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/MutableCookieJar.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.core.network.cookies import androidx.annotation.WorkerThread +import androidx.core.util.Predicate import okhttp3.Cookie import okhttp3.CookieJar import okhttp3.HttpUrl @@ -14,7 +15,7 @@ interface MutableCookieJar : CookieJar { override fun saveFromResponse(url: HttpUrl, cookies: List) @WorkerThread - fun removeCookies(url: HttpUrl) + fun removeCookies(url: HttpUrl, predicate: Predicate?) suspend fun clear(): Boolean } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/PreferencesCookieJar.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/PreferencesCookieJar.kt index bfd95f295..acc7f5eea 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/PreferencesCookieJar.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/cookies/PreferencesCookieJar.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.annotation.WorkerThread import androidx.collection.ArrayMap import androidx.core.content.edit +import androidx.core.util.Predicate import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.Cookie @@ -57,12 +58,14 @@ class PreferencesCookieJar( @Synchronized @WorkerThread - override fun removeCookies(url: HttpUrl) { + override fun removeCookies(url: HttpUrl, predicate: Predicate?) { loadPersistent() val toRemove = HashSet() for ((key, cookie) in cache) { if (cookie.isExpired() || cookie.cookie.matches(url)) { - toRemove += key + if (predicate == null || predicate.test(cookie.cookie)) { + toRemove += key + } } } if (toRemove.isNotEmpty()) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt index 4fcc269d1..687a6d92e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt @@ -49,7 +49,7 @@ class SourceSettingsViewModel @Inject constructor( .scheme("https") .host(repository.domain) .build() - cookieJar.removeCookies(url) + cookieJar.removeCookies(url, null) onActionDone.call(ReversibleAction(R.string.cookies_cleared, null)) loadUsername() } diff --git a/app/src/main/res/menu/opt_captcha.xml b/app/src/main/res/menu/opt_captcha.xml new file mode 100644 index 000000000..8635b227e --- /dev/null +++ b/app/src/main/res/menu/opt_captcha.xml @@ -0,0 +1,11 @@ + + + + + +