diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CloudFlareBlockedException.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CloudFlareBlockedException.kt new file mode 100644 index 000000000..244dba9f1 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CloudFlareBlockedException.kt @@ -0,0 +1,10 @@ +package org.koitharu.kotatsu.core.exceptions + +import okhttp3.Headers +import okio.IOException +import org.koitharu.kotatsu.parsers.model.MangaSource + +class CloudFlareBlockedException( + val url: String, + val source: MangaSource?, +) : IOException("Blocked by CloudFlare") diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt index 8e353bfcb..227a64035 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt @@ -4,6 +4,7 @@ import okhttp3.Interceptor import okhttp3.Response import okhttp3.internal.closeQuietly import org.jsoup.Jsoup +import org.koitharu.kotatsu.core.exceptions.CloudFlareBlockedException import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.parsers.model.MangaSource import java.net.HttpURLConnection.HTTP_FORBIDDEN @@ -17,14 +18,23 @@ class CloudFlareInterceptor : Interceptor { val content = response.body?.let { response.peekBody(Long.MAX_VALUE) }?.byteStream()?.use { Jsoup.parse(it, Charsets.UTF_8.name(), response.request.url.toString()) } ?: return response - if (content.getElementById("challenge-error-title") != null) { + val hasCaptcha = content.getElementById("challenge-error-title") != null + val isBlocked = content.selectFirst("h2[data-translate=\"blocked_why_headline\"]") != null + if (hasCaptcha || isBlocked) { val request = response.request response.closeQuietly() - throw CloudFlareProtectedException( - url = request.url.toString(), - source = request.tag(MangaSource::class.java), - headers = request.headers, - ) + if (isBlocked) { + throw CloudFlareBlockedException( + url = request.url.toString(), + source = request.tag(MangaSource::class.java), + ) + } else { + throw CloudFlareProtectedException( + url = request.url.toString(), + source = request.tag(MangaSource::class.java), + headers = request.headers, + ) + } } } return response diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt index 94726ef95..485d2a6ac 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt @@ -12,6 +12,7 @@ import org.jsoup.HttpStatusException import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException import org.koitharu.kotatsu.core.exceptions.CaughtException +import org.koitharu.kotatsu.core.exceptions.CloudFlareBlockedException import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException import org.koitharu.kotatsu.core.exceptions.NoDataReceivedException @@ -38,6 +39,7 @@ private const val IMAGE_FORMAT_NOT_SUPPORTED = "Image format not supported" fun Throwable.getDisplayMessage(resources: Resources): String = when (this) { is AuthRequiredException -> resources.getString(R.string.auth_required) is CloudFlareProtectedException -> resources.getString(R.string.captcha_required) + is CloudFlareBlockedException -> resources.getString(R.string.blocked_by_server_message) is ActivityNotFoundException, is UnsupportedOperationException, -> resources.getString(R.string.operation_not_supported) @@ -79,6 +81,8 @@ fun Throwable.getDisplayIcon() = when (this) { is SocketTimeoutException, -> R.drawable.ic_plug_large + is CloudFlareBlockedException -> R.drawable.ic_denied_large + else -> R.drawable.ic_error_large } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index f6294c8ae..e1923e1ca 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -40,6 +40,7 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.toErrorFooter import org.koitharu.kotatsu.list.ui.model.toErrorState import org.koitharu.kotatsu.list.ui.model.toUi +import org.koitharu.kotatsu.parsers.exception.NotFoundException import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaSource @@ -88,7 +89,7 @@ open class RemoteListViewModel @Inject constructor( list.isNullOrEmpty() && error != null -> add( error.toErrorState( canRetry = true, - secondaryAction = if (browserUrl != null) R.string.open_in_browser else 0, + secondaryAction = if (error !is NotFoundException && browserUrl != null) R.string.open_in_browser else 0, ), ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 65f0195e2..a275ab7a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -640,4 +640,5 @@ Recent queries Suggested queries Authors + You are blocked by the server. Try to use a different network connection (VPN, Proxy, etc.)