From a2af3a465214ecdd557021469ac3cc0ce2a03b10 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sat, 5 Aug 2023 13:49:37 +0800 Subject: [PATCH] Manually resolve Cloudflare challenge if failed to bypass --- app/src/main/AndroidManifest.xml | 2 + .../exception/CloudflareBypassException.kt | 4 +- .../hippo/ehviewer/ui/ConfigureActivity.kt | 4 +- .../com/hippo/ehviewer/ui/WebViewActivity.kt | 18 ++++++++ ...SignInScreen.kt => WebViewSignInScreen.kt} | 2 +- .../ehviewer/ui/scene/GalleryListScene.kt | 15 ++++++ .../ehviewer/ui/webview/WebViewScreen.kt | 46 +++++++++++++++++++ .../hippo/ehviewer/util/WebViewExtensions.kt | 2 + .../interceptor/CloudflareInterceptor.kt | 3 +- .../network/interceptor/WebViewInterceptor.kt | 4 +- app/src/main/res/navigation/nav_graph.xml | 9 ++++ app/src/main/res/values-zh-rCN/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + 13 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/hippo/ehviewer/ui/WebViewActivity.kt rename app/src/main/java/com/hippo/ehviewer/ui/login/{WebviewSignInScreen.kt => WebViewSignInScreen.kt} (98%) create mode 100644 app/src/main/java/com/hippo/ehviewer/ui/webview/WebViewScreen.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 34c8d8ac5..7e0631a2b 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -135,6 +135,8 @@ android:launchMode="singleTask" android:taskAffinity="" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> + { binding.tip.text = ExceptionUtils.getReadableString(state.error) transition.showView(2) + if (state.error.cause is CloudflareBypassException) { + dialogState.awaitPermissionOrCancel( + android.R.string.ok, + android.R.string.cancel, + R.string.cloudflare_bypass_failed, + ) { + Text(text = stringResource(id = R.string.open_in_webview)) + } + navAnimated(R.id.webView, bundleOf(WebViewActivity.KEY_URL to EhUrl.host)) + } } is LoadState.NotLoading -> { delay(500) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/webview/WebViewScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/webview/WebViewScreen.kt new file mode 100644 index 000000000..9a83c4ec0 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/webview/WebViewScreen.kt @@ -0,0 +1,46 @@ +package com.hippo.ehviewer.ui.webview + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.Modifier +import com.google.accompanist.web.WebView +import com.google.accompanist.web.rememberWebViewState +import com.hippo.ehviewer.client.EhCookieStore +import com.hippo.ehviewer.util.setDefaultSettings + +@Composable +fun WebViewScreen(url: String, onNavigateUp: () -> Unit) { + val state = rememberWebViewState(url = url) + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = state.pageTitle ?: url) }, + navigationIcon = { + IconButton(onClick = onNavigateUp) { + Icon(imageVector = Icons.Default.Close, contentDescription = null) + } + }, + ) + }, + ) { paddingValues -> + WebView( + state = state, + modifier = Modifier.padding(top = paddingValues.calculateTopPadding()).fillMaxSize(), + onCreated = { it.setDefaultSettings() }, + ) + DisposableEffect(Unit) { + onDispose { + EhCookieStore.flush() + } + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/util/WebViewExtensions.kt b/app/src/main/java/com/hippo/ehviewer/util/WebViewExtensions.kt index d68fb01b5..98a08608c 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/WebViewExtensions.kt +++ b/app/src/main/java/com/hippo/ehviewer/util/WebViewExtensions.kt @@ -2,10 +2,12 @@ package com.hippo.ehviewer.util import android.annotation.SuppressLint import android.webkit.WebView +import com.hippo.ehviewer.client.CHROME_USER_AGENT @SuppressLint("SetJavaScriptEnabled") fun WebView.setDefaultSettings() = settings.run { builtInZoomControls = true displayZoomControls = false javaScriptEnabled = true + userAgentString = CHROME_USER_AGENT } diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt index 235b3f7e8..63d75e2ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt @@ -51,7 +51,7 @@ class CloudflareInterceptor(context: Context) : WebViewInterceptor(context) { val headers = parseHeaders(originalRequest.headers) executor.execute { - webview = createWebView(originalRequest) + webview = createWebView() webview?.webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView, url: String) { @@ -102,7 +102,6 @@ class CloudflareInterceptor(context: Context) : WebViewInterceptor(context) { // Throw exception if we failed to bypass Cloudflare if (!cloudflareBypassed) { - // TODO: Prompt user to open url in WebView and finish the challenge manually throw CloudflareBypassException() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt index 5eef8000d..26a6eda2c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt @@ -56,11 +56,9 @@ abstract class WebViewInterceptor(private val context: Context) : Interceptor { await(30, TimeUnit.SECONDS) } - fun createWebView(request: Request): WebView { + fun createWebView(): WebView { return WebView(context).apply { setDefaultSettings() - // Avoid sending empty User-Agent, Chromium WebView will reset to default if empty - settings.userAgentString = request.header("User-Agent") } } } diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 15ff2324e..76d913c8f 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -76,4 +76,13 @@ android:id="@+id/progressScene" android:name="com.hippo.ehviewer.ui.scene.ProgressScene" android:label="ProgressScene" /> + + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4b1e98026..b20b277ad 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -64,6 +64,8 @@ 登录失败 若持续出错,请尝试“通过网页登录”或“通过 Cookie 登录”。 知道了 + 未能绕过 Cloudflare 验证 + 在 WebView 中打开链接以通过验证? 你想选择哪个画廊站点? e-hentai:对任何人开放\nexhentai:仅对登录用户开放 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1240f8d86..88c1e814f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -103,6 +103,8 @@ Sign in failed If this issue continues, try \"Sign in via WebView\" or \"Sign in via cookies\". Got it + Failed to bypass Cloudflare challenge + Open URL in WebView to finish the challenge? Which gallery site do you want to visit? e-hentai: available for anyone\nexhentai: available for who has signed in