浏览代码

Only show Webview update prompt if CF bypass fails

arkon 5 年之前
父节点
当前提交
a50a3df716

+ 40 - 24
app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt

@@ -7,8 +7,11 @@ import android.os.Handler
 import android.os.Looper
 import android.webkit.WebSettings
 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.checkVersion
+import eu.kanade.tachiyomi.util.system.isOutdated
+import eu.kanade.tachiyomi.util.system.toast
 import okhttp3.Cookie
 import okhttp3.HttpUrl.Companion.toHttpUrl
 import okhttp3.Interceptor
@@ -21,8 +24,6 @@ import java.util.concurrent.TimeUnit
 
 class CloudflareInterceptor(private val context: Context) : Interceptor {
 
-    private val serverCheck = arrayOf("cloudflare-nginx", "cloudflare")
-
     private val handler = Handler(Looper.getMainLooper())
 
     private val networkHelper: NetworkHelper by injectLazy()
@@ -44,36 +45,35 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
         val response = chain.proceed(originalRequest)
 
         // Check if Cloudflare anti-bot is on
-        if (response.code == 503 && response.header("Server") in serverCheck) {
-            try {
-                response.close()
-                networkHelper.cookieManager.remove(originalRequest.url, listOf("__cfduid", "cf_clearance"), 0)
-                val oldCookie = networkHelper.cookieManager.get(originalRequest.url)
-                        .firstOrNull { it.name == "cf_clearance" }
-                return if (resolveWithWebView(originalRequest, oldCookie)) {
-                    chain.proceed(originalRequest)
-                } else {
-                    throw IOException("Failed to bypass Cloudflare!")
-                }
-            } catch (e: Exception) {
-                // Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
-                // we don't crash the entire app
-                throw IOException(e)
-            }
+        if (response.code != 503 || response.header("Server") !in SERVER_CHECK) {
+            return response
         }
 
-        return response
+        try {
+            response.close()
+            networkHelper.cookieManager.remove(originalRequest.url, COOKIE_NAMES, 0)
+            val oldCookie = networkHelper.cookieManager.get(originalRequest.url)
+                    .firstOrNull { it.name == "cf_clearance" }
+            resolveWithWebView(originalRequest, oldCookie)
+            return chain.proceed(originalRequest)
+        } catch (e: Exception) {
+            // Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
+            // we don't crash the entire app
+            throw IOException(e)
+        }
     }
 
     @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
         // OkHttp doesn't support asynchronous interceptors.
         val latch = CountDownLatch(1)
 
         var webView: WebView? = null
+
         var challengeFound = false
         var cloudflareBypassed = false
+        var isWebviewOutdated = false
 
         val origRequestUrl = request.url.toString()
         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)
             webView = webview
 
-            webview.checkVersion()
-
             webview.settings.javaScriptEnabled = true
             webview.settings.userAgentString = request.header("User-Agent")
 
@@ -135,10 +133,28 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
         latch.await(12, TimeUnit.SECONDS)
 
         handler.post {
+            if (!cloudflareBypassed) {
+                isWebviewOutdated = webView?.isOutdated() == true
+            }
+
             webView?.stopLoading()
             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")
     }
 
 }

+ 4 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt

@@ -15,7 +15,10 @@ import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.source.online.HttpSource
 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.visible
 import kotlinx.android.synthetic.main.webview_activity.*
@@ -53,8 +56,6 @@ class WebViewActivity : BaseActivity() {
             val url = intent.extras!!.getString(URL_KEY) ?: return
             val headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
 
-            webview.checkVersion()
-
             webview.settings.javaScriptEnabled = true
             webview.settings.userAgentString = source.headers["User-Agent"]
 

+ 3 - 7
app/src/main/java/eu/kanade/tachiyomi/util/system/WebviewUtil.kt

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

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -521,6 +521,7 @@
     <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_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>
 
     <!-- Download Notification -->