From 548c41fbf98108d63bc8d75d63e7f09518320dab Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 2 Jun 2023 19:48:39 +0300 Subject: [PATCH] Proxy authenticaton support #376 --- app/build.gradle | 2 +- .../kotatsu/core/network/CommonHeaders.kt | 1 + .../kotatsu/core/network/NetworkModule.kt | 1 + .../core/network/ProxyAuthenticator.kt | 22 +++++++++++++++++++ .../kotatsu/core/prefs/AppSettings.kt | 9 ++++++++ .../settings/ContentSettingsFragment.kt | 2 +- .../kotatsu/settings/ProxySettingsFragment.kt | 15 +++++++++++++ .../settings/utils/PasswordSummaryProvider.kt | 19 ++++++++++++++++ app/src/main/res/values/strings.xml | 3 +++ app/src/main/res/xml/pref_proxy.xml | 15 +++++++++++++ 10 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/core/network/ProxyAuthenticator.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/PasswordSummaryProvider.kt diff --git a/app/build.gradle b/app/build.gradle index 5e3d86120..094ef4ff5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -79,7 +79,7 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:fa7ea5b16a') { + implementation('com.github.KotatsuApp:kotatsu-parsers:44e28b40d3') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt index f8976acd6..1454542ea 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt @@ -14,6 +14,7 @@ object CommonHeaders { const val ACCEPT_ENCODING = "Accept-Encoding" const val AUTHORIZATION = "Authorization" const val CACHE_CONTROL = "Cache-Control" + const val PROXY_AUTHORIZATION = "Proxy-Authorization" val CACHE_CONTROL_NO_STORE: CacheControl get() = CacheControl.Builder().noStore().build() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt index a60c5db64..1c91966a0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt @@ -59,6 +59,7 @@ interface NetworkModule { writeTimeout(20, TimeUnit.SECONDS) cookieJar(cookieJar) proxySelector(AppProxySelector(settings)) + proxyAuthenticator(ProxyAuthenticator(settings)) dns(DoHManager(cache, settings)) if (settings.isSSLBypassEnabled) { bypassSSLErrors() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/ProxyAuthenticator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/ProxyAuthenticator.kt new file mode 100644 index 000000000..bc1fb8d53 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/ProxyAuthenticator.kt @@ -0,0 +1,22 @@ +package org.koitharu.kotatsu.core.network + +import okhttp3.Authenticator +import okhttp3.Credentials +import okhttp3.Request +import okhttp3.Response +import okhttp3.Route +import org.koitharu.kotatsu.core.prefs.AppSettings + +class ProxyAuthenticator( + private val settings: AppSettings, +) : Authenticator { + + override fun authenticate(route: Route?, response: Response): Request? { + val login = settings.proxyLogin ?: return null + val password = settings.proxyPassword ?: return null + val credential = Credentials.basic(login, password) + return response.request.newBuilder() + .header(CommonHeaders.PROXY_AUTHORIZATION, credential) + .build() + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 0f04f1123..b48123670 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -298,6 +298,12 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { val proxyPort: Int get() = prefs.getString(KEY_PROXY_PORT, null)?.toIntOrNull() ?: 0 + val proxyLogin: String? + get() = prefs.getString(KEY_PROXY_LOGIN, null)?.takeUnless { it.isEmpty() } + + val proxyPassword: String? + get() = prefs.getString(KEY_PROXY_PASSWORD, null)?.takeUnless { it.isEmpty() } + var localListOrder: SortOrder get() = prefs.getEnumValue(KEY_LOCAL_LIST_ORDER, SortOrder.NEWEST) set(value) = prefs.edit { putEnumValue(KEY_LOCAL_LIST_ORDER, value) } @@ -451,6 +457,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_PROXY_TYPE = "proxy_type" const val KEY_PROXY_ADDRESS = "proxy_address" const val KEY_PROXY_PORT = "proxy_port" + const val KEY_PROXY_AUTH = "proxy_auth" + const val KEY_PROXY_LOGIN = "proxy_login" + const val KEY_PROXY_PASSWORD = "proxy_password" const val KEY_IMAGES_PROXY = "images_proxy" // About diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt index e062e6800..9e9091beb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt @@ -146,7 +146,7 @@ class ContentSettingsFragment : summary = if (type == Proxy.Type.DIRECT || address.isNullOrEmpty() || port == 0) { context.getString(R.string.disabled) } else { - "$type $address:$port" + "$address:$port" } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt index 7b86847c4..ab6097f5f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt @@ -6,11 +6,13 @@ import android.view.View import android.view.inputmethod.EditorInfo import androidx.preference.EditTextPreference import androidx.preference.Preference +import androidx.preference.PreferenceCategory import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.settings.utils.EditTextBindListener +import org.koitharu.kotatsu.settings.utils.PasswordSummaryProvider import java.net.Proxy @AndroidEntryPoint @@ -33,6 +35,16 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy), validator = null, ), ) + findPreference(AppSettings.KEY_PROXY_PASSWORD)?.let { pref -> + pref.setOnBindEditTextListener( + EditTextBindListener( + inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD, + hint = null, + validator = null, + ), + ) + pref.summaryProvider = PasswordSummaryProvider() + } updateDependencies() } @@ -56,5 +68,8 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy), val isProxyEnabled = settings.proxyType != Proxy.Type.DIRECT findPreference(AppSettings.KEY_PROXY_ADDRESS)?.isEnabled = isProxyEnabled findPreference(AppSettings.KEY_PROXY_PORT)?.isEnabled = isProxyEnabled + findPreference(AppSettings.KEY_PROXY_AUTH)?.isEnabled = isProxyEnabled + findPreference(AppSettings.KEY_PROXY_LOGIN)?.isEnabled = isProxyEnabled + findPreference(AppSettings.KEY_PROXY_PASSWORD)?.isEnabled = isProxyEnabled } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/PasswordSummaryProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/PasswordSummaryProvider.kt new file mode 100644 index 000000000..5d6068aca --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/PasswordSummaryProvider.kt @@ -0,0 +1,19 @@ +package org.koitharu.kotatsu.settings.utils + +import android.text.TextUtils +import androidx.preference.EditTextPreference +import androidx.preference.Preference + +class PasswordSummaryProvider() : Preference.SummaryProvider { + + private val delegate = EditTextPreference.SimpleSummaryProvider.getInstance() + + override fun provideSummary(preference: EditTextPreference): CharSequence? { + val summary = delegate.provideSummary(preference) + return if (summary != null && !TextUtils.isEmpty(preference.text)) { + String(CharArray(summary.length) { '\u2022' }) + } else { + summary + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ccd1ac74..eb9f3d1f4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -429,4 +429,7 @@ Images optimization proxy Use the wsrv.nl service to reduce traffic usage and speed up image loading if possible Invert colors + Username + Password + Authorization (optional) diff --git a/app/src/main/res/xml/pref_proxy.xml b/app/src/main/res/xml/pref_proxy.xml index 4bf1cad5c..4b0447277 100644 --- a/app/src/main/res/xml/pref_proxy.xml +++ b/app/src/main/res/xml/pref_proxy.xml @@ -21,4 +21,19 @@ android:title="@string/port" app:useSimpleSummaryProvider="true" /> + + + + + + + +