Skip to content

Commit

Permalink
Use a local server to serve Extension files
Browse files Browse the repository at this point in the history
File scheme is not reliable due to security restriction.
  • Loading branch information
JingMatrix committed Jul 22, 2023
1 parent 15665d4 commit 165f297
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 2 deletions.
1 change: 1 addition & 0 deletions app/src/main/assets/extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Browser APIs will be implement in front-end
15 changes: 13 additions & 2 deletions app/src/main/java/org/matrix/chromext/Listener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.json.JSONObject
import org.matrix.chromext.devtools.DEV_FRONT_END
import org.matrix.chromext.devtools.DevToolClient
import org.matrix.chromext.devtools.getInspectPages
import org.matrix.chromext.extension.LocalFiles
import org.matrix.chromext.proxy.ERUD_URL
import org.matrix.chromext.proxy.UserScriptProxy
import org.matrix.chromext.script.ScriptDbHelper
Expand All @@ -25,7 +26,9 @@ object Listener {
val devToolClients = mutableMapOf<Pair<String, String>, Pair<DevToolClient, DevToolClient>>()
val allowedActions =
mapOf(
"front-end" to listOf("inspectPages", "getIds", "getMeta", "updateMeta", "deleteScript"),
"front-end" to
listOf(
"inspectPages", "getIds", "getMeta", "updateMeta", "deleteScript", "extension"),
"devtools" to listOf("websocket"))

private fun parseArray(str: String): Array<String> {
Expand Down Expand Up @@ -72,7 +75,7 @@ object Listener {
runCatching {
val data = JSONObject(text)
val action = data.getString("action")
val payload = data.getString("payload")
val payload = data.optString("payload")
if (checkPermisson(action, currentTab)) {
val callback = on(action, payload, currentTab)
if (callback != null) Chrome.evaluateJavascript(listOf(callback))
Expand Down Expand Up @@ -204,6 +207,14 @@ object Listener {
ScriptDbManager.scripts.removeAll(ScriptDbManager.scripts.filter { ids.contains(it.id) })
dbHelper.close()
}
"extension" -> {
if (payload == "") {
val code =
LocalFiles.script +
"window.dispatchEvent(new CustomEvent('extension', ${LocalFiles.start()}));"
Chrome.evaluateJavascript(listOf(code))
}
}
"websocket" -> {
val detail = JSONObject(payload)
if (!detail.has("tabId")) {
Expand Down
93 changes: 93 additions & 0 deletions app/src/main/java/org/matrix/chromext/extension/LocalFiles.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.matrix.chromext.extension

import java.io.File
import java.io.FileReader
import java.net.ServerSocket
import java.net.Socket
import java.net.URLConnection
import kotlin.concurrent.thread
import org.json.JSONObject
import org.matrix.chromext.Chrome
import org.matrix.chromext.utils.Log

object LocalFiles {

private val directory: File
private val extensions = mutableMapOf<String, JSONObject>()
val script: String

init {
val ctx = Chrome.getContext()
directory = File(ctx.getExternalFilesDir(null), "Extension")
script = ctx.assets.open("extension.js").bufferedReader().use { it.readText() }
if (!directory.exists()) directory.mkdirs()
directory.listFiles()?.forEach {
val path = it.name
val manifest = File(it, "manifest.json")
if (manifest.exists()) {
val json = FileReader(manifest).use { it.readText() }
runCatching { extensions.put(path, JSONObject(json)) }
}
}
}

private fun serveFiles(path: String, connection: Socket) {
runCatching {
Log.d(path + "is requested")
val request =
String(connection.inputStream.readNBytes(DEFAULT_BUFFER_SIZE / 16))
.split("\r\n")[0]
.split(" ")
if (request[0] == "GET" && request[2] == "HTTP/1.1") {
val name = request[1]
val file = File(path + name)
if (file.exists()) {
val data = file.readBytes()
val type = URLConnection.guessContentTypeFromName(name) ?: "text/plain"
val response =
arrayOf(
"HTTP/1.1 200",
"Content-Length: ${data.size}",
"Content-Type: ${type}",
"Access-Control-Allow-Origin: *")
connection.outputStream.write(
(response.joinToString("\r\n") + "\r\n\r\n").toByteArray())
connection.outputStream.write(data)
} else {
connection.outputStream.write("HTTP/1.1 404 Not Found\r\n\r\n".toByteArray())
}
connection.close()
}
}
.onFailure { Log.ex(it) }
}

private fun startServer(name: String) {
if (extensions.containsKey(name) && !extensions.get(name)!!.has("port")) {
val server = ServerSocket()
server.bind(null)
thread {
runCatching {
while (true) {
val socket = server.accept()
serveFiles(directory.toString() + "/" + name, socket)
}
}
.onFailure {
Log.ex(it)
server.close()
if (extensions.get(name)?.optInt("port") == server.getLocalPort()) {
extensions.get(name)!!.remove("port")
}
}
}
extensions.get(name)!!.put("port", server.getLocalPort())
}
}

fun start(): JSONObject {
extensions.keys.forEach { startServer(it) }
val info = JSONObject(extensions as Map<String, JSONObject>)
return JSONObject().put("detail", JSONObject(mapOf("type" to "init", "manifest" to info)))
}
}
1 change: 1 addition & 0 deletions app/src/main/java/org/matrix/chromext/hook/Menu.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ object MenuHook : BaseHook() {
when (ctx.resources.getResourceName(id)) {
"org.matrix.chromext:id/extension_id" -> {
Log.toast(ctx, "WIP support for extensions")
Listener.on("extension")
}
"org.matrix.chromext:id/install_script_id" -> {
if (getUrl().startsWith("https://raw.githubusercontent.com/")) {
Expand Down

0 comments on commit 165f297

Please sign in to comment.