Only show Webview update prompt if CF bypass fails

This commit is contained in:
arkon 2020-02-11 18:36:51 -05:00
parent f29124773b
commit a50a3df716
4 changed files with 48 additions and 34 deletions

View File

@ -7,8 +7,11 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.WebViewClientCompat import eu.kanade.tachiyomi.util.system.WebViewClientCompat
import eu.kanade.tachiyomi.util.system.checkVersion import eu.kanade.tachiyomi.util.system.isOutdated
import eu.kanade.tachiyomi.util.system.toast
import okhttp3.Cookie import okhttp3.Cookie
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor import okhttp3.Interceptor
@ -21,8 +24,6 @@ import java.util.concurrent.TimeUnit
class CloudflareInterceptor(private val context: Context) : Interceptor { class CloudflareInterceptor(private val context: Context) : Interceptor {
private val serverCheck = arrayOf("cloudflare-nginx", "cloudflare")
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private val networkHelper: NetworkHelper by injectLazy() private val networkHelper: NetworkHelper by injectLazy()
@ -44,17 +45,17 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
val response = chain.proceed(originalRequest) val response = chain.proceed(originalRequest)
// Check if Cloudflare anti-bot is on // Check if Cloudflare anti-bot is on
if (response.code == 503 && response.header("Server") in serverCheck) { if (response.code != 503 || response.header("Server") !in SERVER_CHECK) {
return response
}
try { try {
response.close() response.close()
networkHelper.cookieManager.remove(originalRequest.url, listOf("__cfduid", "cf_clearance"), 0) networkHelper.cookieManager.remove(originalRequest.url, COOKIE_NAMES, 0)
val oldCookie = networkHelper.cookieManager.get(originalRequest.url) val oldCookie = networkHelper.cookieManager.get(originalRequest.url)
.firstOrNull { it.name == "cf_clearance" } .firstOrNull { it.name == "cf_clearance" }
return if (resolveWithWebView(originalRequest, oldCookie)) { resolveWithWebView(originalRequest, oldCookie)
chain.proceed(originalRequest) return chain.proceed(originalRequest)
} else {
throw IOException("Failed to bypass Cloudflare!")
}
} catch (e: Exception) { } catch (e: Exception) {
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that // Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
// we don't crash the entire app // we don't crash the entire app
@ -62,18 +63,17 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
} }
} }
return response
}
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
private fun resolveWithWebView(request: Request, oldCookie: Cookie?): Boolean { private fun resolveWithWebView(request: Request, oldCookie: Cookie?) {
// We need to lock this thread until the WebView finds the challenge solution url, because // We need to lock this thread until the WebView finds the challenge solution url, because
// OkHttp doesn't support asynchronous interceptors. // OkHttp doesn't support asynchronous interceptors.
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
var webView: WebView? = null var webView: WebView? = null
var challengeFound = false var challengeFound = false
var cloudflareBypassed = false var cloudflareBypassed = false
var isWebviewOutdated = false
val origRequestUrl = request.url.toString() val origRequestUrl = request.url.toString()
val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
@ -82,8 +82,6 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
val webview = WebView(context) val webview = WebView(context)
webView = webview webView = webview
webview.checkVersion()
webview.settings.javaScriptEnabled = true webview.settings.javaScriptEnabled = true
webview.settings.userAgentString = request.header("User-Agent") webview.settings.userAgentString = request.header("User-Agent")
@ -135,10 +133,28 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
latch.await(12, TimeUnit.SECONDS) latch.await(12, TimeUnit.SECONDS)
handler.post { handler.post {
if (!cloudflareBypassed) {
isWebviewOutdated = webView?.isOutdated() == true
}
webView?.stopLoading() webView?.stopLoading()
webView?.destroy() webView?.destroy()
} }
return cloudflareBypassed
// Throw exception if we failed to bypass Cloudflare
if (!cloudflareBypassed) {
// Prompt user to update WebView if it seems too outdated
if (isWebviewOutdated) {
context.toast(R.string.information_webview_outdated, Toast.LENGTH_LONG)
}
throw Exception(context.getString(R.string.information_cloudflare_bypass_failure))
}
}
companion object {
private val SERVER_CHECK = arrayOf("cloudflare-nginx", "cloudflare")
private val COOKIE_NAMES = listOf("__cfduid", "cf_clearance")
} }
} }

View File

@ -15,7 +15,10 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.util.system.* import eu.kanade.tachiyomi.util.system.WebViewClientCompat
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.invisible import eu.kanade.tachiyomi.util.view.invisible
import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.webview_activity.* import kotlinx.android.synthetic.main.webview_activity.*
@ -53,8 +56,6 @@ class WebViewActivity : BaseActivity() {
val url = intent.extras!!.getString(URL_KEY) ?: return val url = intent.extras!!.getString(URL_KEY) ?: return
val headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } val headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
webview.checkVersion()
webview.settings.javaScriptEnabled = true webview.settings.javaScriptEnabled = true
webview.settings.userAgentString = source.headers["User-Agent"] webview.settings.userAgentString = source.headers["User-Agent"]

View File

@ -1,19 +1,15 @@
package eu.kanade.tachiyomi.util.system package eu.kanade.tachiyomi.util.system
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast
import eu.kanade.tachiyomi.R
private val WEBVIEW_UA_VERSION_REGEX by lazy { private val WEBVIEW_UA_VERSION_REGEX by lazy {
Regex(""".*Chrome/(\d+)\..*""") Regex(""".*Chrome/(\d+)\..*""")
} }
private const val MINIMUM_WEBVIEW_VERSION = 72 private const val MINIMUM_WEBVIEW_VERSION = 70
fun WebView.checkVersion() { fun WebView.isOutdated(): Boolean {
if (getWebviewMajorVersion(this) < MINIMUM_WEBVIEW_VERSION) { return getWebviewMajorVersion(this) < MINIMUM_WEBVIEW_VERSION
this.context.toast(R.string.information_webview_outdated, Toast.LENGTH_LONG)
}
} }
// Based on https://stackoverflow.com/a/29218966 // Based on https://stackoverflow.com/a/29218966

View File

@ -521,6 +521,7 @@
<string name="information_no_recent_manga">No recently read manga</string> <string name="information_no_recent_manga">No recently read manga</string>
<string name="information_empty_library">Your library is empty, add series to your library from the catalogues.</string> <string name="information_empty_library">Your library is empty, add series to your library from the catalogues.</string>
<string name="information_empty_category">You have no categories. Hit the plus button to create one for organizing your library.</string> <string name="information_empty_category">You have no categories. Hit the plus button to create one for organizing your library.</string>
<string name="information_cloudflare_bypass_failure">Failed to bypass Cloudflare</string>
<string name="information_webview_outdated">Please update the WebView app for better compatibility</string> <string name="information_webview_outdated">Please update the WebView app for better compatibility</string>
<!-- Download Notification --> <!-- Download Notification -->