diff --git a/app/src/main/assets/GM.js b/app/src/main/assets/GM.js index 3edd782a..10249ef1 100644 --- a/app/src/main/assets/GM.js +++ b/app/src/main/assets/GM.js @@ -130,6 +130,7 @@ const GM_cookie = new (class { return; } if (cookies == this.store && url.origin != location.origin) return; + const capitalize = (s) => s && s[0].toUpperCase() + s.slice(1); return cookies .filter((item) => { if (!("name" in item && "value" in item)) return false; @@ -138,14 +139,32 @@ const GM_cookie = new (class { if ("domain" in item) { let domain = item.domain; if (domain.startsWith(".")) domain = domain.slice(1); - if (!url.hostname.endsWith(item.domain)) return false; + if (!url.hostname.endsWith(domain)) return false; } const expires = item.expirationDate || item.expires; if (expires > 0) return expires * 1000 > new Date().getTime(); return true; }) - .map((item) => item.name + "=" + item.value) - .join("; "); + .map((item) => { + let header = [item.name + "=" + item.value]; + header.push(`Domain=${item.domain}`); + if (Number.isFinite(item.expires) && item.expires != -1) { + const date = new Date(); + date.setTime(item.expires * 1000); + header.push(`expires=${date.toUTCString()}`); + } + const props = ["path", "sameSite", "httpOnly", "secure"]; + for (const prop of props) { + if (!(prop in item)) continue; + const val = item[prop]; + if (typeof val == "string" && val.length != 0) { + header.push(capitalize(prop) + `=${capitalize(val)}`); + } else if (val === true) { + header.push(capitalize(prop)); + } + } + return header.join("; "); + }); } async list(details = { url: window.origin }, callback) { let cookies, error; @@ -647,7 +666,6 @@ function GM_xmlhttpRequest(details) { const origin = new URL(details.url).origin; if ( - !("permission" in navigator) && location.origin == origin && GM_info.script.grants.includes("GM_cookie") && !("cookie" in details) && @@ -658,7 +676,10 @@ function GM_xmlhttpRequest(details) { } request.cookie = GM_cookie.export(details.url); } - + if (typeof request.cookie == "string") { + request.cookie = request.cookie.split("; "); + } + if (!Array.isArray(request.cookie)) delete request.cookie; ChromeXt.dispatch("xmlhttpRequest", { id: GM_info.script.id, request, @@ -763,7 +784,7 @@ class ResponseSink { const key = parts.shift().toLowerCase(); var value = parts.join("="); if (key === "expires") { - cookie.expires = new Date(value).getTime() / 1000; + cookie.expires = Date.parse(value).getTime() / 1000; } else if (key === "max-age") { cookie.maxAge = Number(value); cookie.expires = cookie.maxAge + new Date().getTime() / 1000; @@ -809,17 +830,6 @@ class ResponseSink { this.xhr.error = new Error("Redirection not allowed"); this.xhr.abort(); } - if ( - "permission" in navigator && - GM_info.script.includes("GM_cookie") && - new URL(this.xhr.finalUrl).origin == location.origin - ) { - const cookies = ResponseSink.parseCookie( - this.xhr.headers.getSetCookie(), - this.xhr.finalUrl - ); - GM_cookie.set(cookies, undefined, true); - } this.xhr.total = this.xhr.headers.get("Content-Length"); if (this.xhr.total !== null) { this.xhr.lengthComputable = true; diff --git a/app/src/main/java/org/matrix/chromext/Chrome.kt b/app/src/main/java/org/matrix/chromext/Chrome.kt index ddd79057..91bea8be 100644 --- a/app/src/main/java/org/matrix/chromext/Chrome.kt +++ b/app/src/main/java/org/matrix/chromext/Chrome.kt @@ -8,6 +8,7 @@ import android.os.Build import android.os.Handler import java.io.File import java.lang.ref.WeakReference +import java.net.CookieManager import java.util.concurrent.Executors import org.json.JSONObject import org.matrix.chromext.devtools.DevSessions @@ -39,6 +40,7 @@ object Chrome { "ChromeXt", "UserScript Notifications", NotificationManager.IMPORTANCE_DEFAULT) .apply { description = "Notifications created by GM_notification API" } } else null + val cookieStore = CookieManager().getCookieStore() fun init(ctx: Context, packageName: String? = null) { val initialized = mContext != null diff --git a/app/src/main/java/org/matrix/chromext/hook/UserScript.kt b/app/src/main/java/org/matrix/chromext/hook/UserScript.kt index 6de09715..1be2ea08 100644 --- a/app/src/main/java/org/matrix/chromext/hook/UserScript.kt +++ b/app/src/main/java/org/matrix/chromext/hook/UserScript.kt @@ -2,8 +2,6 @@ package org.matrix.chromext.hook import android.content.Context import android.net.http.HttpResponseCache -import java.net.CookieHandler -import java.net.CookieManager import org.matrix.chromext.Chrome import org.matrix.chromext.Listener import org.matrix.chromext.proxy.UserScriptProxy @@ -19,7 +17,6 @@ object UserScriptHook : BaseHook() { override fun init() { val proxy = UserScriptProxy - CookieHandler.setDefault(CookieManager()) // proxy.tabModelJniBridge.declaredConstructors[0].hookAfter { // Chrome.addTabModel(it.thisObject) diff --git a/app/src/main/java/org/matrix/chromext/utils/XMLHttpRequest.kt b/app/src/main/java/org/matrix/chromext/utils/XMLHttpRequest.kt index f86160ce..2c5baa21 100644 --- a/app/src/main/java/org/matrix/chromext/utils/XMLHttpRequest.kt +++ b/app/src/main/java/org/matrix/chromext/utils/XMLHttpRequest.kt @@ -2,9 +2,7 @@ package org.matrix.chromext.utils import android.util.Base64 import java.io.IOException -import java.net.CookieHandler -import java.net.CookieManager -import java.net.CookiePolicy +import java.net.HttpCookie import java.net.HttpURLConnection import java.net.SocketTimeoutException import java.net.URL @@ -12,39 +10,38 @@ import org.json.JSONArray import org.json.JSONObject import org.matrix.chromext.Chrome import org.matrix.chromext.Listener -import org.matrix.chromext.hook.UserScriptHook -import org.matrix.chromext.hook.WebViewHook import org.matrix.chromext.script.Local class XMLHttpRequest(id: String, request: JSONObject, uuid: Double, currentTab: Any?) { - val response = JSONObject(mapOf("id" to id, "uuid" to uuid)) - val request = request val currentTab = currentTab + val request = request + val response = JSONObject(mapOf("id" to id, "uuid" to uuid)) + var connection: HttpURLConnection? = null + var cookies: List - val url = URL(request.optString("url")) - val method = request.optString("method") - val headers = request.optJSONObject("headers") - val followRedirects = request.optString("redirect") != "error" - val binary = request.optBoolean("binary") val anonymous = request.optBoolean("anonymous") + val binary = request.optBoolean("binary") + val buffersize = request.optInt("buffersize", 8) + val cookie = request.optJSONArray("cookie") + val followRedirects = request.optString("redirect") != "error" + val headers = request.optJSONObject("headers") + val method = request.optString("method") val nocache = request.optBoolean("nocache") val timeout = request.optInt("timeout") - val buffersize = request.optInt("buffersize", 8) val responseType = request.optString("responseType") + val url = URL(request.optString("url")) + val uri = url.toURI() init { - if (UserScriptHook.isInit) { - val manager = CookieHandler.getDefault() as CookieManager - if (anonymous || request.has("cookie")) { - if (anonymous) manager.setCookiePolicy(CookiePolicy.ACCEPT_NONE) - val uri = url.toURI() - val cookieStore = manager.getCookieStore() - cookieStore.get(uri).forEach { cookieStore.remove(uri, it) } - } else { - manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL) + if (cookie != null && !anonymous) { + for (i in 0 until cookie.length()) { + runCatching { + HttpCookie.parse(cookie!!.getString(i)).forEach { Chrome.cookieStore.add(uri, it) } + } } } + cookies = Chrome.cookieStore.get(uri) } fun abort() { @@ -59,12 +56,9 @@ class XMLHttpRequest(id: String, request: JSONObject, uuid: Double, currentTab: setInstanceFollowRedirects(followRedirects) setUseCaches(!nocache) setConnectTimeout(timeout) - if (request.has("cookie") && !anonymous) - setRequestProperty("Cookie", request.optString("cookie")) - if (WebViewHook.isInit && !anonymous) { - val manger = android.webkit.CookieManager.getInstance() - addRequestProperty("Cookie", manger.getCookie(url.toString())) - } + + if (!anonymous && cookies.size > 0) + setRequestProperty("Cookie", cookies.map { it.toString() }.joinToString("; ")) if (request.has("user")) { val user = request.optString("user") @@ -93,12 +87,15 @@ class XMLHttpRequest(id: String, request: JSONObject, uuid: Double, currentTab: headers.containsKey("Content-Encoding") || (headers.get("Content-Type")?.optString(0, "")?.contains("charset") == true) data.put("binary", binary) - if (WebViewHook.isInit && !anonymous) { - val manager = android.webkit.CookieManager.getInstance() + + if (!anonymous) { headerFields .filter { it.key != null && it.key.startsWith("Set-Cookie") } - .forEach { it.value.forEach { manager.setCookie(url.toString(), it) } } - manager.flush() + .forEach { + it.value.forEach { + HttpCookie.parse(it).forEach { Chrome.cookieStore.add(uri, it) } + } + } } val buffer = ByteArray(buffersize * DEFAULT_BUFFER_SIZE)