Use system CookieManager as CookieJar
This commit is contained in:
@@ -1,23 +1,19 @@
|
||||
package org.koitharu.kotatsu.browser.cloudflare
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.koitharu.kotatsu.core.network.AndroidCookieJar
|
||||
import org.koitharu.kotatsu.core.network.WebViewClientCompat
|
||||
|
||||
class CloudFlareClient(
|
||||
private val cookieJar: CookieJar,
|
||||
private val cookieJar: AndroidCookieJar,
|
||||
private val callback: CloudFlareCallback,
|
||||
private val targetUrl: String
|
||||
) : WebViewClient() {
|
||||
|
||||
private val cookieManager = CookieManager.getInstance()
|
||||
) : WebViewClientCompat() {
|
||||
|
||||
init {
|
||||
cookieManager.removeAllCookies(null)
|
||||
cookieJar.remove(targetUrl, CF_UID, CF_CLEARANCE)
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
|
||||
@@ -36,16 +32,11 @@ class CloudFlareClient(
|
||||
}
|
||||
|
||||
private fun checkClearance() {
|
||||
val httpUrl = targetUrl.toHttpUrl()
|
||||
val rawCookie = cookieManager.getCookie(targetUrl) ?: return
|
||||
val cookies = rawCookie.split(';').mapNotNull {
|
||||
Cookie.parse(httpUrl, it)
|
||||
val cookies = cookieJar.loadForRequest(targetUrl.toHttpUrl())
|
||||
if (cookies.any { it.name == CF_CLEARANCE }) {
|
||||
callback.onCheckPassed()
|
||||
}
|
||||
if (cookies.none { it.name == CF_CLEARANCE }) {
|
||||
return
|
||||
}
|
||||
cookieJar.saveFromResponse(httpUrl, cookies)
|
||||
callback.onCheckPassed()
|
||||
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.koitharu.kotatsu.core.network
|
||||
|
||||
import android.webkit.CookieManager
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class AndroidCookieJar : CookieJar {
|
||||
|
||||
private val cookieManager = CookieManager.getInstance()
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
val rawCookie = cookieManager.getCookie(url.toString()) ?: return emptyList()
|
||||
return rawCookie.split(';').mapNotNull {
|
||||
Cookie.parse(url, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
if (cookies.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val urlString = url.toString()
|
||||
for (cookie in cookies) {
|
||||
cookieManager.setCookie(urlString, cookie.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(url: String, vararg names: String) {
|
||||
val cookies = cookieManager.getCookie(url) ?: return
|
||||
val newCookies = cookies.split(";")
|
||||
.filterNot { cookie ->
|
||||
names.any { cookie.startsWith("$it=") }
|
||||
}.joinToString(";")
|
||||
cookieManager.setCookie(url, newCookies)
|
||||
}
|
||||
|
||||
fun clearAsync() {
|
||||
cookieManager.removeAllCookies(null)
|
||||
}
|
||||
|
||||
suspend fun clear() = suspendCoroutine<Boolean> { continuation ->
|
||||
cookieManager.removeAllCookies(continuation::resume)
|
||||
}
|
||||
}
|
||||
@@ -6,21 +6,12 @@ import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.core.network.cookies.ClearableCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.PersistentCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.cache.SetCookieCache
|
||||
import org.koitharu.kotatsu.core.network.cookies.persistence.SharedPrefsCookiePersistor
|
||||
import org.koitharu.kotatsu.utils.CacheUtils
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val networkModule
|
||||
get() = module {
|
||||
single<CookieJar> {
|
||||
PersistentCookieJar(
|
||||
SetCookieCache(),
|
||||
SharedPrefsCookiePersistor(androidContext())
|
||||
)
|
||||
} bind ClearableCookieJar::class
|
||||
single { AndroidCookieJar() } bind CookieJar::class
|
||||
single(named(CacheUtils.QUALIFIER_HTTP)) { CacheUtils.createHttpCache(androidContext()) }
|
||||
single {
|
||||
OkHttpClient.Builder().apply {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.koitharu.kotatsu.core.network
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.os.Build
|
||||
import android.webkit.*
|
||||
|
||||
@Suppress("OverridingDeprecatedMember")
|
||||
abstract class WebViewClientCompat : WebViewClient() {
|
||||
|
||||
open fun shouldOverrideUrlCompat(view: WebView, url: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
open fun shouldInterceptRequestCompat(view: WebView, url: String): WebResourceResponse? {
|
||||
return null
|
||||
}
|
||||
|
||||
open fun onReceivedErrorCompat(
|
||||
view: WebView,
|
||||
errorCode: Int,
|
||||
description: String?,
|
||||
failingUrl: String,
|
||||
isMainFrame: Boolean
|
||||
) {
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
final override fun shouldOverrideUrlLoading(
|
||||
view: WebView,
|
||||
request: WebResourceRequest
|
||||
): Boolean = shouldOverrideUrlCompat(view, request.url.toString())
|
||||
|
||||
final override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||
return shouldOverrideUrlCompat(view, url)
|
||||
}
|
||||
|
||||
final override fun shouldInterceptRequest(
|
||||
view: WebView,
|
||||
request: WebResourceRequest
|
||||
): WebResourceResponse? = shouldInterceptRequestCompat(view, request.url.toString())
|
||||
|
||||
final override fun shouldInterceptRequest(
|
||||
view: WebView,
|
||||
url: String
|
||||
): WebResourceResponse? = shouldInterceptRequestCompat(view, url)
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
final override fun onReceivedError(
|
||||
view: WebView,
|
||||
request: WebResourceRequest,
|
||||
error: WebResourceError
|
||||
) {
|
||||
onReceivedErrorCompat(
|
||||
view,
|
||||
error.errorCode,
|
||||
error.description?.toString(),
|
||||
request.url.toString(),
|
||||
request.isForMainFrame
|
||||
)
|
||||
}
|
||||
|
||||
final override fun onReceivedError(
|
||||
view: WebView,
|
||||
errorCode: Int,
|
||||
description: String?,
|
||||
failingUrl: String
|
||||
) {
|
||||
onReceivedErrorCompat(view, errorCode, description, failingUrl, failingUrl == view.url)
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
final override fun onReceivedHttpError(
|
||||
view: WebView,
|
||||
request: WebResourceRequest,
|
||||
error: WebResourceResponse
|
||||
) {
|
||||
onReceivedErrorCompat(
|
||||
view,
|
||||
error.statusCode,
|
||||
error.reasonPhrase,
|
||||
request.url
|
||||
.toString(),
|
||||
request.isForMainFrame
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies
|
||||
|
||||
import okhttp3.CookieJar
|
||||
|
||||
/**
|
||||
* This interface extends [okhttp3.CookieJar] and adds methods to clear the cookies.
|
||||
*/
|
||||
interface ClearableCookieJar : CookieJar {
|
||||
|
||||
/**
|
||||
* Clear all the session cookies while maintaining the persisted ones.
|
||||
*/
|
||||
fun clearSession()
|
||||
|
||||
/**
|
||||
* Clear all the cookies from persistence and from the cache.
|
||||
*/
|
||||
fun clear()
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies
|
||||
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl
|
||||
import org.koitharu.kotatsu.core.network.cookies.cache.CookieCache
|
||||
import org.koitharu.kotatsu.core.network.cookies.persistence.CookiePersistor
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class PersistentCookieJar(
|
||||
private val cache: CookieCache,
|
||||
private val persistor: CookiePersistor
|
||||
) : ClearableCookieJar {
|
||||
|
||||
private var isLoaded = AtomicBoolean(false)
|
||||
|
||||
@Synchronized
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
if (isLoaded.compareAndSet(false, true)) {
|
||||
cache.addAll(persistor.loadAll())
|
||||
}
|
||||
cache.addAll(cookies)
|
||||
persistor.saveAll(cookies.filter { it.persistent })
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
if (isLoaded.compareAndSet(false, true)) {
|
||||
cache.addAll(persistor.loadAll())
|
||||
}
|
||||
val cookiesToRemove: MutableList<Cookie> = ArrayList()
|
||||
val validCookies: MutableList<Cookie> = ArrayList()
|
||||
val it = cache.iterator()
|
||||
while (it.hasNext()) {
|
||||
val currentCookie = it.next()
|
||||
when {
|
||||
currentCookie.isExpired() -> {
|
||||
cookiesToRemove.add(currentCookie)
|
||||
it.remove()
|
||||
}
|
||||
currentCookie.matches(url) -> {
|
||||
validCookies.add(currentCookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
persistor.removeAll(cookiesToRemove)
|
||||
return validCookies
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun clearSession() {
|
||||
cache.clear()
|
||||
cache.addAll(persistor.loadAll())
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun clear() {
|
||||
cache.clear()
|
||||
persistor.clear()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
fun Cookie.isExpired(): Boolean {
|
||||
return expiresAt < System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies.cache
|
||||
|
||||
import okhttp3.Cookie
|
||||
|
||||
/**
|
||||
* A CookieCache handles the volatile cookie session storage.
|
||||
*/
|
||||
interface CookieCache : MutableIterable<Cookie> {
|
||||
/**
|
||||
* Add all the new cookies to the session, existing cookies will be overwritten.
|
||||
*
|
||||
* @param newCookies
|
||||
*/
|
||||
fun addAll(newCookies: Collection<Cookie>)
|
||||
|
||||
/**
|
||||
* Clear all the cookies from the session.
|
||||
*/
|
||||
fun clear()
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies.cache
|
||||
|
||||
import okhttp3.Cookie
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* This class decorates a Cookie to re-implements equals() and hashcode() methods in order to identify
|
||||
* the cookie by the following attributes: name, domain, path, secure & hostOnly.
|
||||
*
|
||||
*
|
||||
*
|
||||
* This new behaviour will be useful in determining when an already existing cookie in session must be overwritten.
|
||||
*/
|
||||
internal class IdentifiableCookie(val cookie: Cookie) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is IdentifiableCookie) return false
|
||||
return other.cookie.name == cookie.name && other.cookie.domain == cookie.domain
|
||||
&& other.cookie.path == cookie.path && other.cookie.secure == cookie.secure
|
||||
&& other.cookie.hostOnly == cookie.hostOnly
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var hash = 17
|
||||
hash = 31 * hash + cookie.name.hashCode()
|
||||
hash = 31 * hash + cookie.domain.hashCode()
|
||||
hash = 31 * hash + cookie.path.hashCode()
|
||||
hash = 31 * hash + if (cookie.secure) 0 else 1
|
||||
hash = 31 * hash + if (cookie.hostOnly) 0 else 1
|
||||
return hash
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun decorateAll(cookies: Collection<Cookie>): List<IdentifiableCookie> {
|
||||
val identifiableCookies: MutableList<IdentifiableCookie> = ArrayList(cookies.size)
|
||||
for (cookie in cookies) {
|
||||
identifiableCookies.add(IdentifiableCookie(cookie))
|
||||
}
|
||||
return identifiableCookies
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies.cache
|
||||
|
||||
import okhttp3.Cookie
|
||||
import org.koitharu.kotatsu.core.network.cookies.cache.IdentifiableCookie.Companion.decorateAll
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class SetCookieCache : CookieCache {
|
||||
|
||||
private val cookies: MutableSet<IdentifiableCookie> =
|
||||
Collections.newSetFromMap(ConcurrentHashMap())
|
||||
|
||||
override fun addAll(newCookies: Collection<Cookie>) {
|
||||
for (cookie in decorateAll(newCookies)) {
|
||||
cookies.remove(cookie)
|
||||
cookies.add(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
cookies.clear()
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<Cookie> = SetCookieCacheIterator()
|
||||
|
||||
private inner class SetCookieCacheIterator : MutableIterator<Cookie> {
|
||||
|
||||
private val iterator = cookies.iterator()
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return iterator.hasNext()
|
||||
}
|
||||
|
||||
override fun next(): Cookie {
|
||||
return iterator.next().cookie
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
iterator.remove()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies.persistence
|
||||
|
||||
import okhttp3.Cookie
|
||||
|
||||
/**
|
||||
* A CookiePersistor handles the persistent cookie storage.
|
||||
*/
|
||||
interface CookiePersistor {
|
||||
|
||||
fun loadAll(): List<Cookie>
|
||||
|
||||
/**
|
||||
* Persist all cookies, existing cookies will be overwritten.
|
||||
*
|
||||
* @param cookies cookies persist
|
||||
*/
|
||||
fun saveAll(cookies: Collection<Cookie>)
|
||||
|
||||
/**
|
||||
* Removes indicated cookies from persistence.
|
||||
*
|
||||
* @param cookies cookies to remove from persistence
|
||||
*/
|
||||
fun removeAll(cookies: Collection<Cookie>)
|
||||
|
||||
/**
|
||||
* Clear all cookies from persistence.
|
||||
*/
|
||||
fun clear()
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies.persistence
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.Cookie
|
||||
import java.io.*
|
||||
|
||||
class SerializableCookie : Serializable {
|
||||
|
||||
@Transient
|
||||
private var cookie: Cookie? = null
|
||||
|
||||
fun encode(cookie: Cookie?): String? {
|
||||
this.cookie = cookie
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
var objectOutputStream: ObjectOutputStream? = null
|
||||
try {
|
||||
objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
|
||||
objectOutputStream.writeObject(this)
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "IOException in encodeCookie", e)
|
||||
return null
|
||||
} finally {
|
||||
if (objectOutputStream != null) {
|
||||
try { // Closing a ByteArrayOutputStream has no effect, it can be used later (and is used in the return statement)
|
||||
objectOutputStream.close()
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "Stream not closed in encodeCookie", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return byteArrayToHexString(byteArrayOutputStream.toByteArray())
|
||||
}
|
||||
|
||||
fun decode(encodedCookie: String): Cookie? {
|
||||
val bytes = hexStringToByteArray(encodedCookie)
|
||||
val byteArrayInputStream = ByteArrayInputStream(
|
||||
bytes
|
||||
)
|
||||
var cookie: Cookie? = null
|
||||
var objectInputStream: ObjectInputStream? = null
|
||||
try {
|
||||
objectInputStream = ObjectInputStream(byteArrayInputStream)
|
||||
cookie = (objectInputStream.readObject() as SerializableCookie).cookie
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "IOException in decodeCookie", e)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
Log.d(TAG, "ClassNotFoundException in decodeCookie", e)
|
||||
} finally {
|
||||
if (objectInputStream != null) {
|
||||
try {
|
||||
objectInputStream.close()
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "Stream not closed in decodeCookie", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookie
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun writeObject(out: ObjectOutputStream) {
|
||||
out.writeObject(cookie!!.name)
|
||||
out.writeObject(cookie!!.value)
|
||||
out.writeLong(if (cookie!!.persistent) cookie!!.expiresAt else NON_VALID_EXPIRES_AT)
|
||||
out.writeObject(cookie!!.domain)
|
||||
out.writeObject(cookie!!.path)
|
||||
out.writeBoolean(cookie!!.secure)
|
||||
out.writeBoolean(cookie!!.httpOnly)
|
||||
out.writeBoolean(cookie!!.hostOnly)
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ClassNotFoundException::class)
|
||||
private fun readObject(`in`: ObjectInputStream) {
|
||||
val builder = Cookie.Builder()
|
||||
builder.name((`in`.readObject() as String))
|
||||
builder.value((`in`.readObject() as String))
|
||||
val expiresAt = `in`.readLong()
|
||||
if (expiresAt != NON_VALID_EXPIRES_AT) {
|
||||
builder.expiresAt(expiresAt)
|
||||
}
|
||||
val domain = `in`.readObject() as String
|
||||
builder.domain(domain)
|
||||
builder.path((`in`.readObject() as String))
|
||||
if (`in`.readBoolean()) builder.secure()
|
||||
if (`in`.readBoolean()) builder.httpOnly()
|
||||
if (`in`.readBoolean()) builder.hostOnlyDomain(domain)
|
||||
cookie = builder.build()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
private val TAG = SerializableCookie::class.java.simpleName
|
||||
|
||||
const val serialVersionUID = -8594045714036645534L
|
||||
private const val NON_VALID_EXPIRES_AT = -1L
|
||||
|
||||
/**
|
||||
* Using some super basic byte array <-> hex conversions so we don't
|
||||
* have to rely on any large Base64 libraries. Can be overridden if you
|
||||
* like!
|
||||
*
|
||||
* @param bytes byte array to be converted
|
||||
* @return string containing hex values
|
||||
*/
|
||||
private fun byteArrayToHexString(bytes: ByteArray): String {
|
||||
val sb = StringBuilder(bytes.size * 2)
|
||||
for (element in bytes) {
|
||||
val v: Int = element.toInt() and 0xff
|
||||
if (v < 16) {
|
||||
sb.append('0')
|
||||
}
|
||||
sb.append(Integer.toHexString(v))
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts hex values from strings to byte array
|
||||
*
|
||||
* @param hexString string of hex-encoded values
|
||||
* @return decoded byte array
|
||||
*/
|
||||
private fun hexStringToByteArray(hexString: String): ByteArray {
|
||||
val len = hexString.length
|
||||
val data = ByteArray(len / 2)
|
||||
var i = 0
|
||||
while (i < len) {
|
||||
data[i / 2] = ((Character.digit(hexString[i], 16) shl 4) + Character
|
||||
.digit(hexString[i + 1], 16)).toByte()
|
||||
i += 2
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Francisco José Montiel Navarro.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.koitharu.kotatsu.core.network.cookies.persistence
|
||||
|
||||
import android.content.Context
|
||||
import okhttp3.Cookie
|
||||
import java.util.*
|
||||
|
||||
class SharedPrefsCookiePersistor(context: Context) : CookiePersistor {
|
||||
|
||||
private val sharedPreferences by lazy {
|
||||
context.getSharedPreferences("cookies", Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
override fun loadAll(): List<Cookie> {
|
||||
val cookies: MutableList<Cookie> = ArrayList(sharedPreferences.all.size)
|
||||
for ((_, value) in sharedPreferences.all) {
|
||||
val serializedCookie = value as? String
|
||||
if (serializedCookie != null) {
|
||||
val cookie = SerializableCookie().decode(serializedCookie)
|
||||
if (cookie != null) {
|
||||
cookies.add(cookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookies
|
||||
}
|
||||
|
||||
override fun saveAll(cookies: Collection<Cookie>) {
|
||||
val editor = sharedPreferences.edit()
|
||||
for (cookie in cookies) {
|
||||
editor.putString(createCookieKey(cookie), SerializableCookie().encode(cookie))
|
||||
}
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
override fun removeAll(cookies: Collection<Cookie>) {
|
||||
val editor = sharedPreferences.edit()
|
||||
for (cookie in cookies) {
|
||||
editor.remove(createCookieKey(cookie))
|
||||
}
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
sharedPreferences.edit().clear().apply()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
fun createCookieKey(cookie: Cookie): String {
|
||||
return (if (cookie.secure) "https" else "http") + "://" + cookie.domain + cookie.path + "|" + cookie.name
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import org.koin.android.ext.android.get
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.network.cookies.ClearableCookieJar
|
||||
import org.koitharu.kotatsu.core.network.AndroidCookieJar
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.local.data.Cache
|
||||
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||
@@ -75,10 +75,8 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
||||
}
|
||||
AppSettings.KEY_COOKIES_CLEAR -> {
|
||||
viewLifecycleScope.launch {
|
||||
val cookieJar = get<ClearableCookieJar>()
|
||||
withContext(Dispatchers.IO) {
|
||||
cookieJar.clear()
|
||||
}
|
||||
val cookieJar = get<AndroidCookieJar>()
|
||||
cookieJar.clear()
|
||||
Snackbar.make(
|
||||
listView ?: return@launch,
|
||||
R.string.cookies_cleared,
|
||||
|
||||
Reference in New Issue
Block a user