Migrate cookies store to CookieManager backend
This commit is contained in:
1 parent
ad04681460
commit
55b3753e66
10 files changed
+81
-389
No files matched your search
@@ -37,6 +37,7 @@ import com.hippo.ehviewer.dailycheck.checkDawn
|
|||||||
import com.hippo.ehviewer.dao.EhDatabase
|
import com.hippo.ehviewer.dao.EhDatabase
|
||||||
import com.hippo.ehviewer.download.DownloadManager
|
import com.hippo.ehviewer.download.DownloadManager
|
||||||
import com.hippo.ehviewer.legacy.cleanObsoleteCache
|
import com.hippo.ehviewer.legacy.cleanObsoleteCache
|
||||||
|
import com.hippo.ehviewer.legacy.migrateCookies
|
||||||
import com.hippo.ehviewer.okhttp.cache
|
import com.hippo.ehviewer.okhttp.cache
|
||||||
import com.hippo.ehviewer.okhttp.httpClient
|
import com.hippo.ehviewer.okhttp.httpClient
|
||||||
import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
||||||
@@ -76,6 +77,9 @@ class EhApplication : Application(), ImageLoaderFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
launchIO {
|
launchIO {
|
||||||
|
launchIO {
|
||||||
|
migrateCookies()
|
||||||
|
}
|
||||||
launchIO {
|
launchIO {
|
||||||
EhTagDatabase.update()
|
EhTagDatabase.update()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,45 +15,42 @@
|
|||||||
*/
|
*/
|
||||||
package com.hippo.ehviewer.client
|
package com.hippo.ehviewer.client
|
||||||
|
|
||||||
import com.hippo.ehviewer.network.CookieDatabase
|
import android.webkit.CookieManager
|
||||||
import com.hippo.ehviewer.network.CookieSet
|
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
|
||||||
import okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
import okhttp3.CookieJar
|
import okhttp3.CookieJar
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import splitties.init.appCtx
|
|
||||||
import java.util.Collections
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
object EhCookieStore : CookieJar {
|
object EhCookieStore : CookieJar {
|
||||||
private val db: CookieDatabase = CookieDatabase(appCtx)
|
private val manager = CookieManager.getInstance()
|
||||||
private val map: MutableMap<String, CookieSet> = db.allCookies
|
fun signOut() = manager.removeAllCookies(null)
|
||||||
|
fun contains(url: HttpUrl, name: String) = get(url).any { it.name == name }
|
||||||
|
|
||||||
fun signOut() {
|
fun get(url: HttpUrl): List<Cookie> {
|
||||||
clear()
|
val cookies = manager.getCookie(url.toString())
|
||||||
|
|
||||||
|
return if (cookies != null && cookies.isNotEmpty()) {
|
||||||
|
cookies.split(";").mapNotNull { Cookie.parse(url, it) }.filterNot { it.name == KEY_UTMP_NAME }
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasSignedIn(): Boolean {
|
fun hasSignedIn(): Boolean {
|
||||||
val url = EhUrl.HOST_E.toHttpUrl()
|
val url = EhUrl.HOST_E.toHttpUrl()
|
||||||
return contains(url, KEY_IPB_MEMBER_ID) &&
|
return contains(url, KEY_IPB_MEMBER_ID) && contains(url, KEY_IPB_PASS_HASH)
|
||||||
contains(url, KEY_IPB_PASS_HASH)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const val KEY_IPB_MEMBER_ID = "ipb_member_id"
|
const val KEY_IPB_MEMBER_ID = "ipb_member_id"
|
||||||
const val KEY_IPB_PASS_HASH = "ipb_pass_hash"
|
const val KEY_IPB_PASS_HASH = "ipb_pass_hash"
|
||||||
const val KEY_IGNEOUS = "igneous"
|
const val KEY_IGNEOUS = "igneous"
|
||||||
const val KEY_SETTINGS_PROFILE = "sp"
|
const val KEY_SETTINGS_PROFILE = "sp"
|
||||||
const val KEY_STAR = "star"
|
private const val KEY_STAR = "star"
|
||||||
private const val KEY_CONTENT_WARNING = "nw"
|
private const val KEY_CONTENT_WARNING = "nw"
|
||||||
private const val CONTENT_WARNING_NOT_SHOW = "1"
|
private const val CONTENT_WARNING_NOT_SHOW = "1"
|
||||||
private val sTipsCookie: Cookie = Cookie.Builder()
|
private const val KEY_UTMP_NAME = "__utmp"
|
||||||
.name(KEY_CONTENT_WARNING)
|
private val sTipsCookie: Cookie = Cookie.Builder().name(KEY_CONTENT_WARNING).value(CONTENT_WARNING_NOT_SHOW).domain(EhUrl.DOMAIN_E).path("/").expiresAt(Long.MAX_VALUE).build()
|
||||||
.value(CONTENT_WARNING_NOT_SHOW)
|
|
||||||
.domain(EhUrl.DOMAIN_E)
|
|
||||||
.path("/")
|
|
||||||
.expiresAt(Long.MAX_VALUE)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
fun newCookie(
|
fun newCookie(
|
||||||
cookie: Cookie,
|
cookie: Cookie,
|
||||||
@@ -87,64 +84,22 @@ object EhCookieStore : CookieJar {
|
|||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyCookie(domain: String, newDomain: String, name: String, path: String = "/") {
|
fun copyNecessaryCookies() {
|
||||||
val cookie = map[domain]?.get(name, domain, path)
|
val cookie = get(EhUrl.HOST_E.toHttpUrl()).filter { it.name == KEY_STAR || it.name == KEY_IPB_MEMBER_ID || it.name == KEY_IPB_PASS_HASH || it.name == KEY_IGNEOUS }
|
||||||
cookie?.let { addCookie(newCookie(it, newDomain)) }
|
cookie.forEach { manager.setCookie(EhUrl.HOST_EX, it.toString()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteCookie(url: HttpUrl, name: String) {
|
fun deleteCookie(url: HttpUrl, name: String) {
|
||||||
val deletedCookie = Cookie.Builder()
|
val deletedCookie = Cookie.Builder().name(name).value("deleted").domain(url.host).expiresAt(0).build()
|
||||||
.name(name)
|
manager.setCookie(url.toString(), deletedCookie.toString())
|
||||||
.value("deleted")
|
|
||||||
.domain(url.host)
|
|
||||||
.expiresAt(0)
|
|
||||||
.build()
|
|
||||||
addCookie(deletedCookie)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun addCookie(cookie: Cookie) {
|
fun addCookie(cookie: Cookie) {
|
||||||
// For cookie database
|
if (EhUrl.DOMAIN_E in cookie.domain) manager.setCookie(EhUrl.HOST_E, cookie.toString()) else manager.setCookie(EhUrl.DOMAIN_EX, cookie.toString())
|
||||||
var toAdd: Cookie? = null
|
|
||||||
var toUpdate: Cookie? = null
|
|
||||||
var toRemove: Cookie? = null
|
|
||||||
var set = map[cookie.domain]
|
|
||||||
if (set == null) {
|
|
||||||
set = CookieSet()
|
|
||||||
map[cookie.domain] = set
|
|
||||||
}
|
|
||||||
if (cookie.expiresAt <= System.currentTimeMillis()) {
|
|
||||||
toRemove = set.remove(cookie)
|
|
||||||
// If the cookie is not persistent, it's not in database
|
|
||||||
if (toRemove != null && !toRemove.persistent) {
|
|
||||||
toRemove = null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
toAdd = cookie
|
|
||||||
toUpdate = set.add(cookie)
|
|
||||||
// If the cookie is not persistent, it's not in database
|
|
||||||
if (!toAdd.persistent) toAdd = null
|
|
||||||
if (toUpdate != null && !toUpdate.persistent) toUpdate = null
|
|
||||||
// Remove the cookie if it updates to null
|
|
||||||
if (toAdd == null && toUpdate != null) {
|
|
||||||
toRemove = toUpdate
|
|
||||||
toUpdate = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toRemove != null) {
|
|
||||||
db.remove(toRemove)
|
|
||||||
}
|
|
||||||
if (toAdd != null) {
|
|
||||||
if (toUpdate != null) {
|
|
||||||
db.update(toUpdate, toAdd)
|
|
||||||
} else {
|
|
||||||
db.add(toAdd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCookieHeader(url: HttpUrl): String {
|
fun getCookieHeader(url: HttpUrl): String {
|
||||||
val cookies = getCookies(url)
|
val cookies = get(url)
|
||||||
val cookieHeader = StringBuilder()
|
val cookieHeader = StringBuilder()
|
||||||
var i = 0
|
var i = 0
|
||||||
val size = cookies.size
|
val size = cookies.size
|
||||||
@@ -159,56 +114,8 @@ object EhCookieStore : CookieJar {
|
|||||||
return cookieHeader.toString()
|
return cookieHeader.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
|
||||||
fun getCookies(url: HttpUrl): List<Cookie> {
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) = cookies.filterNot { it.name == KEY_UTMP_NAME }.forEach { manager.setCookie(url.toString(), it.toString()) }
|
||||||
val accepted: MutableList<Cookie> = ArrayList()
|
|
||||||
val expired: MutableList<Cookie> = ArrayList()
|
|
||||||
for ((domain, cookieSet) in map) {
|
|
||||||
if (domainMatch(url, domain)) {
|
|
||||||
cookieSet[url, accepted, expired]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (cookie in expired) {
|
|
||||||
if (cookie.persistent) {
|
|
||||||
db.remove(cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RFC 6265 Section-5.4 step 2, sort the cookie-list
|
|
||||||
// Cookies with longer paths are listed before cookies with shorter paths.
|
|
||||||
// Ignore creation-time, we don't store them.
|
|
||||||
accepted.sortWith { o1: Cookie, o2: Cookie -> o2.path.length - o1.path.length }
|
|
||||||
return accepted
|
|
||||||
}
|
|
||||||
|
|
||||||
fun contains(url: HttpUrl, name: String?): Boolean {
|
|
||||||
for (cookie in getCookies(url)) {
|
|
||||||
if (cookie.name == name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all cookies in this `CookieRepository`.
|
|
||||||
*/
|
|
||||||
@Synchronized
|
|
||||||
fun clear() {
|
|
||||||
map.clear()
|
|
||||||
launchIO {
|
|
||||||
db.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
|
||||||
for (cookie in cookies) {
|
|
||||||
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
|
|
||||||
if (cookie.name != "__utmp") {
|
|
||||||
addCookie(cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation
|
* Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation
|
||||||
@@ -221,8 +128,7 @@ object EhCookieStore : CookieJar {
|
|||||||
* addresses nor hostnames; they will be verified as IP addresses (which is a more strict
|
* addresses nor hostnames; they will be verified as IP addresses (which is a more strict
|
||||||
* verification).
|
* verification).
|
||||||
*/
|
*/
|
||||||
private val VERIFY_AS_IP_ADDRESS =
|
private val VERIFY_AS_IP_ADDRESS = Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)")
|
||||||
Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if `host` is not a host name and might be an IP address.
|
* Returns true if `host` is not a host name and might be an IP address.
|
||||||
@@ -238,32 +144,19 @@ object EhCookieStore : CookieJar {
|
|||||||
return if (urlHost == domain) {
|
return if (urlHost == domain) {
|
||||||
true // As in 'example.com' matching 'example.com'.
|
true // As in 'example.com' matching 'example.com'.
|
||||||
} else {
|
} else {
|
||||||
urlHost.endsWith(domain!!) &&
|
urlHost.endsWith(domain!!) && urlHost[urlHost.length - domain.length - 1] == '.' && !verifyAsIpAddress(urlHost)
|
||||||
urlHost[urlHost.length - domain.length - 1] == '.' && !verifyAsIpAddress(
|
|
||||||
urlHost,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// As in 'example.com' matching 'www.example.com'.
|
// As in 'example.com' matching 'www.example.com'.
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||||
val cookies = getCookies(url)
|
|
||||||
val checkTips = domainMatch(url, EhUrl.DOMAIN_E)
|
val checkTips = domainMatch(url, EhUrl.DOMAIN_E)
|
||||||
return if (checkTips) {
|
return get(url).run {
|
||||||
val result: MutableList<Cookie> = ArrayList(cookies.size + 1)
|
if (checkTips) {
|
||||||
// Add all but skip some
|
filterNot { it.name == KEY_CONTENT_WARNING }.toMutableList().apply { add(sTipsCookie) }
|
||||||
for (cookie in cookies) {
|
} else {
|
||||||
val name = cookie.name
|
this
|
||||||
if (KEY_CONTENT_WARNING == name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result.add(cookie)
|
|
||||||
}
|
}
|
||||||
// Add some
|
|
||||||
result.add(sTipsCookie)
|
|
||||||
Collections.unmodifiableList(result)
|
|
||||||
} else {
|
|
||||||
cookies
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.hippo.ehviewer.legacy
|
||||||
|
|
||||||
|
import arrow.fx.coroutines.release
|
||||||
|
import arrow.fx.coroutines.resource
|
||||||
|
import com.hippo.ehviewer.client.EhCookieStore
|
||||||
|
import com.hippo.ehviewer.dao.Cookie
|
||||||
|
import com.hippo.ehviewer.dao.CookiesDatabase
|
||||||
|
import splitties.arch.room.roomDb
|
||||||
|
import splitties.init.appCtx
|
||||||
|
|
||||||
|
private const val legacyCookiesDB = "okhttp3-cookie.db"
|
||||||
|
|
||||||
|
private fun Cookie.toOkHttp3Cookie(): okhttp3.Cookie {
|
||||||
|
return okhttp3.Cookie.Builder().apply {
|
||||||
|
name(name)
|
||||||
|
value(value)
|
||||||
|
expiresAt(expiresAt)
|
||||||
|
if (hostOnly) hostOnlyDomain(domain) else domain(domain)
|
||||||
|
path(path)
|
||||||
|
if (secure) secure()
|
||||||
|
if (httpOnly) httpOnly()
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun migrateCookies() {
|
||||||
|
if (legacyCookiesDB !in appCtx.databaseList()) return
|
||||||
|
resource {
|
||||||
|
roomDb<CookiesDatabase>(legacyCookiesDB)
|
||||||
|
} release {
|
||||||
|
it.close()
|
||||||
|
appCtx.deleteDatabase(legacyCookiesDB)
|
||||||
|
} use { database ->
|
||||||
|
database.cookiesDao().list().forEach {
|
||||||
|
EhCookieStore.addCookie(it.toOkHttp3Cookie())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 Hippo Seven
|
|
||||||
*
|
|
||||||
* 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 com.hippo.ehviewer.network
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.hippo.ehviewer.dao.Cookie
|
|
||||||
import com.hippo.ehviewer.dao.CookiesDatabase
|
|
||||||
import splitties.arch.room.roomDb
|
|
||||||
import okhttp3.Cookie as OkHttpCookie
|
|
||||||
|
|
||||||
class CookieDatabase(context: Context) {
|
|
||||||
private val cookiesList by lazy {
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
db.cookiesDao().list().mapNotNull {
|
|
||||||
it.takeUnless { !it.persistent || it.expiresAt <= now } ?: run {
|
|
||||||
db.cookiesDao().delete(it)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}.toMutableList()
|
|
||||||
}
|
|
||||||
private val db = roomDb<CookiesDatabase>("okhttp3-cookie.db")
|
|
||||||
|
|
||||||
val allCookies by lazy {
|
|
||||||
hashMapOf<String, CookieSet>().also { map ->
|
|
||||||
cookiesList.map { it.toOkHttp3Cookie() }.forEach {
|
|
||||||
val set = map[it.domain] ?: CookieSet().apply { map[it.domain] = this }
|
|
||||||
set.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findCookieWithOkHttpCookies(cookie: OkHttpCookie): Cookie? {
|
|
||||||
return cookiesList.find { it.name == cookie.name && it.domain == cookie.domain && it.value == cookie.value }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun add(cookie: OkHttpCookie) {
|
|
||||||
val c = cookie.toCookie()
|
|
||||||
c.id = db.cookiesDao().insert(c)
|
|
||||||
cookiesList.add(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun update(from: OkHttpCookie, to: OkHttpCookie) {
|
|
||||||
val origin = findCookieWithOkHttpCookies(from) ?: return
|
|
||||||
val new = to.toCookie(origin.id)
|
|
||||||
cookiesList.remove(origin)
|
|
||||||
cookiesList.add(new)
|
|
||||||
db.cookiesDao().update(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(cookie: OkHttpCookie) {
|
|
||||||
val origin = findCookieWithOkHttpCookies(cookie) ?: return
|
|
||||||
db.cookiesDao().delete(origin)
|
|
||||||
cookiesList.remove(origin)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
db.clearAllTables()
|
|
||||||
cookiesList.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Cookie.toOkHttp3Cookie(): OkHttpCookie {
|
|
||||||
return OkHttpCookie.Builder().apply {
|
|
||||||
name(name)
|
|
||||||
value(value)
|
|
||||||
expiresAt(expiresAt)
|
|
||||||
if (hostOnly) hostOnlyDomain(domain) else domain(domain)
|
|
||||||
path(path)
|
|
||||||
if (secure) secure()
|
|
||||||
if (httpOnly) httpOnly()
|
|
||||||
}.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun OkHttpCookie.toCookie(id: Long? = null): Cookie {
|
|
||||||
return Cookie(
|
|
||||||
name, value, expiresAt, domain, path, secure, httpOnly, persistent, hostOnly, id,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 Hippo Seven
|
|
||||||
*
|
|
||||||
* 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 com.hippo.ehviewer.network
|
|
||||||
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
|
|
||||||
class CookieSet {
|
|
||||||
private val map: MutableMap<Key, Cookie> = HashMap()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a cookie to this `CookieSet`.
|
|
||||||
* Returns a previous cookie with
|
|
||||||
* the same name, domain and path or `null`.
|
|
||||||
*/
|
|
||||||
fun add(cookie: Cookie): Cookie? {
|
|
||||||
return map.put(Key(cookie), cookie)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a cookie with the same name,
|
|
||||||
* domain and path as the cookie.
|
|
||||||
* Returns the removed cookie or `null`.
|
|
||||||
*/
|
|
||||||
fun remove(cookie: Cookie): Cookie? {
|
|
||||||
return map.remove(Key(cookie))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cookies for the url. Fill `accepted` and `expired`.
|
|
||||||
*/
|
|
||||||
operator fun get(url: HttpUrl, accepted: MutableList<Cookie>, expired: MutableList<Cookie>) {
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
val iterator = map.entries.iterator()
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
val cookie = iterator.next().value
|
|
||||||
if (cookie.expiresAt <= now) {
|
|
||||||
iterator.remove()
|
|
||||||
expired.add(cookie)
|
|
||||||
} else if (cookie.matches(url)) {
|
|
||||||
accepted.add(cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun get(name: String, domain: String, path: String) = map[Key(name, domain, path)]
|
|
||||||
|
|
||||||
internal data class Key(
|
|
||||||
val name: String,
|
|
||||||
val domain: String,
|
|
||||||
val path: String,
|
|
||||||
) {
|
|
||||||
constructor(cookie: Cookie) : this(cookie.name, cookie.domain, cookie.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,7 @@ suspend fun postLogin() = coroutineScope {
|
|||||||
runCatching {
|
runCatching {
|
||||||
// For the `star` cookie
|
// For the `star` cookie
|
||||||
EhEngine.getNews(false)
|
EhEngine.getNews(false)
|
||||||
EhCookieStore.copyCookie(EhUrl.DOMAIN_E, EhUrl.DOMAIN_EX, EhCookieStore.KEY_STAR)
|
EhCookieStore.copyNecessaryCookies()
|
||||||
|
|
||||||
// Sad panda check
|
// Sad panda check
|
||||||
Settings.gallerySite = EhUrl.SITE_EX
|
Settings.gallerySite = EhUrl.SITE_EX
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ import com.hippo.ehviewer.ui.SELECT_SITE_ROUTE_NAME
|
|||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import okhttp3.Cookie
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
@@ -34,63 +32,24 @@ fun WebviewSignInScreen() {
|
|||||||
val client = remember {
|
val client = remember {
|
||||||
object : AccompanistWebViewClient() {
|
object : AccompanistWebViewClient() {
|
||||||
private var present = false
|
private var present = false
|
||||||
fun parseCookies(url: HttpUrl?, cookieStrings: String?): List<Cookie> {
|
|
||||||
if (cookieStrings == null) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
var cookies: MutableList<Cookie>? = null
|
|
||||||
val pieces =
|
|
||||||
cookieStrings.split(";".toRegex()).dropLastWhile { it.isEmpty() }
|
|
||||||
.toTypedArray()
|
|
||||||
for (piece in pieces) {
|
|
||||||
val cookie = Cookie.parse(url!!, piece) ?: continue
|
|
||||||
if (cookies == null) {
|
|
||||||
cookies = ArrayList()
|
|
||||||
}
|
|
||||||
cookies.add(cookie)
|
|
||||||
}
|
|
||||||
return cookies ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addCookie(domain: String, cookie: Cookie) {
|
|
||||||
EhCookieStore.addCookie(
|
|
||||||
EhCookieStore.newCookie(
|
|
||||||
cookie,
|
|
||||||
domain,
|
|
||||||
forcePersistent = true,
|
|
||||||
forceLongLive = true,
|
|
||||||
forceNotHostOnly = true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPageFinished(view: WebView, url: String?) {
|
override fun onPageFinished(view: WebView, url: String?) {
|
||||||
if (present) {
|
if (present) {
|
||||||
view.destroy()
|
view.destroy()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val httpUrl = url?.toHttpUrl() ?: return
|
|
||||||
val cookieString = CookieManager.getInstance().getCookie(EhUrl.HOST_E)
|
|
||||||
val cookies = parseCookies(httpUrl, cookieString)
|
|
||||||
var getId = false
|
var getId = false
|
||||||
var getHash = false
|
var getHash = false
|
||||||
for (cookie in cookies) {
|
EhCookieStore.loadForRequest(EhUrl.HOST_E.toHttpUrl()).forEach {
|
||||||
if (EhCookieStore.KEY_IPB_MEMBER_ID == cookie.name) {
|
if (EhCookieStore.KEY_IPB_MEMBER_ID == it.name) {
|
||||||
getId = true
|
getId = true
|
||||||
} else if (EhCookieStore.KEY_IPB_PASS_HASH == cookie.name) {
|
} else if (EhCookieStore.KEY_IPB_PASS_HASH == it.name) {
|
||||||
getHash = true
|
getHash = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (getId && getHash) {
|
if (getId && getHash) {
|
||||||
present = true
|
present = true
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
val canEx = withNonCancellableContext {
|
val canEx = withNonCancellableContext { postLogin() }
|
||||||
cookies.forEach {
|
|
||||||
addCookie(EhUrl.DOMAIN_EX, it)
|
|
||||||
addCookie(EhUrl.DOMAIN_E, it)
|
|
||||||
}
|
|
||||||
postLogin()
|
|
||||||
}
|
|
||||||
withUIContext { navController.navigate(if (canEx) SELECT_SITE_ROUTE_NAME else FINISH_ROUTE_NAME) }
|
withUIContext { navController.navigate(if (canEx) SELECT_SITE_ROUTE_NAME else FINISH_ROUTE_NAME) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ fun EhScreen() {
|
|||||||
title = stringResource(id = R.string.account_name),
|
title = stringResource(id = R.string.account_name),
|
||||||
summary = Settings.displayName ?: touristMode,
|
summary = Settings.displayName ?: touristMode,
|
||||||
) {
|
) {
|
||||||
val eCookies = EhCookieStore.getCookies(EhUrl.HOST_E.toHttpUrl())
|
val eCookies = EhCookieStore.loadForRequest(EhUrl.HOST_E.toHttpUrl())
|
||||||
val exCookies = EhCookieStore.getCookies(EhUrl.HOST_EX.toHttpUrl())
|
val exCookies = EhCookieStore.loadForRequest(EhUrl.HOST_EX.toHttpUrl())
|
||||||
var ipbMemberId: String? = null
|
var ipbMemberId: String? = null
|
||||||
var ipbPassHash: String? = null
|
var ipbPassHash: String? = null
|
||||||
var igneous: String? = null
|
var igneous: String? = null
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.hippo.ehviewer.ui.settings
|
package com.hippo.ehviewer.ui.settings
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.webkit.CookieManager
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -12,16 +11,13 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.SideEffect
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.google.accompanist.web.WebView
|
import com.google.accompanist.web.WebView
|
||||||
import com.google.accompanist.web.rememberWebViewState
|
import com.google.accompanist.web.rememberWebViewState
|
||||||
import com.hippo.ehviewer.R
|
import com.hippo.ehviewer.R
|
||||||
import com.hippo.ehviewer.client.EhCookieStore
|
|
||||||
import com.hippo.ehviewer.client.EhUrl
|
import com.hippo.ehviewer.client.EhUrl
|
||||||
import com.hippo.ehviewer.ui.LocalNavController
|
import com.hippo.ehviewer.ui.LocalNavController
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
@Composable
|
@Composable
|
||||||
@@ -52,16 +48,5 @@ fun MyTagsScreen() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
SideEffect {
|
|
||||||
CookieManager.getInstance().apply {
|
|
||||||
flush()
|
|
||||||
removeAllCookies(null)
|
|
||||||
removeSessionCookies(null)
|
|
||||||
// Copy cookies from okhttp cookie store to CookieManager
|
|
||||||
EhCookieStore.getCookies(url.toHttpUrl()).forEach {
|
|
||||||
setCookie(url, it.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.hippo.ehviewer.ui.settings
|
package com.hippo.ehviewer.ui.settings
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.webkit.CookieManager
|
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -29,8 +28,6 @@ import com.hippo.ehviewer.client.EhCookieStore
|
|||||||
import com.hippo.ehviewer.client.EhCookieStore.KEY_SETTINGS_PROFILE
|
import com.hippo.ehviewer.client.EhCookieStore.KEY_SETTINGS_PROFILE
|
||||||
import com.hippo.ehviewer.client.EhUrl
|
import com.hippo.ehviewer.client.EhUrl
|
||||||
import com.hippo.ehviewer.ui.LocalNavController
|
import com.hippo.ehviewer.ui.LocalNavController
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
|
||||||
private const val applyJs = "javascript:(function(){var apply = document.getElementById(\"apply\").children[0];apply.click();})();"
|
private const val applyJs = "javascript:(function(){var apply = document.getElementById(\"apply\").children[0];apply.click();})();"
|
||||||
@@ -81,33 +78,9 @@ fun UConfigScreen() {
|
|||||||
val applyTip = stringResource(id = R.string.apply_tip)
|
val applyTip = stringResource(id = R.string.apply_tip)
|
||||||
LaunchedEffect(Unit) { snackbarHostState.showSnackbar(applyTip) }
|
LaunchedEffect(Unit) { snackbarHostState.showSnackbar(applyTip) }
|
||||||
DisposableEffect(Unit) {
|
DisposableEffect(Unit) {
|
||||||
CookieManager.getInstance().apply {
|
|
||||||
flush()
|
|
||||||
removeAllCookies(null)
|
|
||||||
removeSessionCookies(null)
|
|
||||||
// Copy cookies from okhttp cookie store to CookieManager
|
|
||||||
EhCookieStore.getCookies(url.toHttpUrl()).forEach {
|
|
||||||
setCookie(url, it.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onDispose {
|
onDispose {
|
||||||
// Put cookies back to okhttp cookie store
|
EhCookieStore.deleteCookie(EhUrl.host.toHttpUrl(), KEY_SETTINGS_PROFILE)
|
||||||
val cookiesString = CookieManager.getInstance().getCookie(url)
|
|
||||||
if (cookiesString.isNotBlank()) {
|
|
||||||
val hostUrl = EhUrl.host.toHttpUrl()
|
|
||||||
launchIO {
|
|
||||||
EhCookieStore.deleteCookie(hostUrl, KEY_SETTINGS_PROFILE)
|
|
||||||
// The cookies saved in the uconfig page should not be shared between e and ex
|
|
||||||
for (header in cookiesString.split(";".toRegex()).dropLastWhile { it.isEmpty() }) {
|
|
||||||
Cookie.parse(hostUrl, header)?.let {
|
|
||||||
EhCookieStore.addCookie(longLive(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun longLive(cookie: Cookie) = Cookie.Builder().name(cookie.name).value(cookie.value).domain(cookie.domain).path(cookie.path).expiresAt(Long.MAX_VALUE).build()
|
|
||||||
Reference in new issue
Block a user