Fix CloudFlare protection detection (close #1129)
This commit is contained in:
@@ -16,8 +16,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 675
|
versionCode = 676
|
||||||
versionName = '7.6.2'
|
versionName = '7.6.3'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
||||||
ksp {
|
ksp {
|
||||||
@@ -82,7 +82,7 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation('com.github.KotatsuApp:kotatsu-parsers:6f7e1fcfb2') {
|
implementation('com.github.KotatsuApp:kotatsu-parsers:1ebb298cd7') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.koitharu.kotatsu.core.ui.BaseActivity
|
|||||||
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
import org.koitharu.kotatsu.core.util.ext.configureForParser
|
||||||
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import com.google.android.material.R as materialR
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
@@ -175,8 +176,7 @@ class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCal
|
|||||||
|
|
||||||
private suspend fun clearCfCookies(url: HttpUrl) = runInterruptible(Dispatchers.Default) {
|
private suspend fun clearCfCookies(url: HttpUrl) = runInterruptible(Dispatchers.Default) {
|
||||||
cookieJar.removeCookies(url) { cookie ->
|
cookieJar.removeCookies(url) { cookie ->
|
||||||
val name = cookie.name
|
CloudFlareHelper.isCloudFlareCookie(cookie.name)
|
||||||
name.startsWith("cf_") || name.startsWith("_cf") || name.startsWith("__cf") || name == "csrftoken"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ package org.koitharu.kotatsu.browser.cloudflare
|
|||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
||||||
import org.koitharu.kotatsu.browser.BrowserClient
|
import org.koitharu.kotatsu.browser.BrowserClient
|
||||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||||
|
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
|
||||||
|
|
||||||
private const val CF_CLEARANCE = "cf_clearance"
|
|
||||||
private const val LOOP_COUNTER = 3
|
private const val LOOP_COUNTER = 3
|
||||||
|
|
||||||
class CloudFlareClient(
|
class CloudFlareClient(
|
||||||
@@ -50,8 +49,5 @@ class CloudFlareClient(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getClearance(): String? {
|
private fun getClearance() = CloudFlareHelper.getClearanceCookie(cookieJar, targetUrl)
|
||||||
return cookieJar.loadForRequest(targetUrl.toHttpUrl())
|
|
||||||
.find { it.name == CF_CLEARANCE }?.value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,41 +2,43 @@ package org.koitharu.kotatsu.core.network
|
|||||||
|
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.internal.closeQuietly
|
import okio.IOException
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareBlockedException
|
import org.koitharu.kotatsu.core.exceptions.CloudFlareBlockedException
|
||||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import java.net.HttpURLConnection.HTTP_FORBIDDEN
|
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
|
||||||
import java.net.HttpURLConnection.HTTP_UNAVAILABLE
|
|
||||||
|
|
||||||
class CloudFlareInterceptor : Interceptor {
|
class CloudFlareInterceptor : Interceptor {
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val response = chain.proceed(chain.request())
|
val request = chain.request()
|
||||||
if (response.code == HTTP_FORBIDDEN || response.code == HTTP_UNAVAILABLE) {
|
val response = chain.proceed(request)
|
||||||
val content = response.body?.let { response.peekBody(Long.MAX_VALUE) }?.byteStream()?.use {
|
return when (CloudFlareHelper.checkResponseForProtection(response)) {
|
||||||
Jsoup.parse(it, Charsets.UTF_8.name(), response.request.url.toString())
|
CloudFlareHelper.PROTECTION_BLOCKED -> response.closeThrowing(
|
||||||
} ?: return response
|
CloudFlareBlockedException(
|
||||||
val hasCaptcha = content.getElementById("challenge-error-title") != null
|
url = request.url.toString(),
|
||||||
val isBlocked = content.selectFirst("h2[data-translate=\"blocked_why_headline\"]") != null
|
source = request.tag(MangaSource::class.java),
|
||||||
if (hasCaptcha || isBlocked) {
|
),
|
||||||
val request = response.request
|
)
|
||||||
response.closeQuietly()
|
|
||||||
if (isBlocked) {
|
CloudFlareHelper.PROTECTION_CAPTCHA -> response.closeThrowing(
|
||||||
throw CloudFlareBlockedException(
|
CloudFlareProtectedException(
|
||||||
url = request.url.toString(),
|
url = request.url.toString(),
|
||||||
source = request.tag(MangaSource::class.java),
|
source = request.tag(MangaSource::class.java),
|
||||||
)
|
headers = request.headers,
|
||||||
} else {
|
),
|
||||||
throw CloudFlareProtectedException(
|
)
|
||||||
url = request.url.toString(),
|
|
||||||
source = request.tag(MangaSource::class.java),
|
else -> response
|
||||||
headers = request.headers,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return response
|
}
|
||||||
|
|
||||||
|
private fun Response.closeThrowing(error: IOException): Nothing {
|
||||||
|
try {
|
||||||
|
close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
error.addSuppressed(e)
|
||||||
|
}
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user