Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F10360507
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
135 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/android/code/index.mjs b/android/code/index.mjs
index c47b8d9..1c1fd34 100644
--- a/android/code/index.mjs
+++ b/android/code/index.mjs
@@ -1,128 +1,164 @@
import child_process from "child_process";
import fs from "fs";
import { Server } from "socket.io";
async function spawnPromise(program, args) {
return new Promise((resolve, _reject) => {
let output = "";
const process = child_process.spawn(program, args);
process.stdout.on("data", (data) => {
output += data;
});
process.stderr.on("data", (data) => {
output += data;
});
process.on("close", (code) => {
resolve({ output, code });
});
});
}
let io = new Server();
async function send_private_data() {
- let adid = fs.readFileSync("/adid").toString();
+ let adid = await spawnPromise("bash", ["/conf/get_adid.sh"])
+ adid = adid.output;
let gps_coords = await spawnPromise("bash", ["/conf/get_location.sh"])
gps_coords = gps_coords.output;
gps_coords = gps_coords.trim().split(',');
io.emit("private_info", {adid, latitude: gps_coords[0], longitude: gps_coords[1]})
}
function send_notification(socket, is_ok, context, message) {
socket.emit("notification", {
is_ok,
context,
message,
});
}
let screenshot_in_flight = false;
+let gps_setting_in_progress = false;
//maybe check output of child processes and send errors in some way
io.on("connection", (socket) => {
socket.onAny((ev, ...args) => {
console.log("server got: ", ev, ...args);
});
socket.on("screenshot", async () => {
if (screenshot_in_flight)
return;
screenshot_in_flight = true;
- let screen = await fetch("http://localhost:9987/v2/uiDevice/screenshot");
+ let screen;
+ try {
+ screen = await fetch("http://localhost:9987/v2/uiDevice/screenshot");
+ } catch(err) {
+ console.error("Failed to get the screenshot from culebra, the emulator probably died", err);
+ screenshot_in_flight = false;
+ return;
+ }
let body = await screen.bytes();
socket.emit("screenshot_data", body);
screenshot_in_flight = false;
});
socket.on("private_info_req", async () => {
await send_private_data();
})
socket.on("reset_adid", async () => {
await spawnPromise("bash", ["/conf/reset_adid.sh"]);
await send_private_data();
})
socket.on("back", async () => {
+ if (gps_setting_in_progress) {
+ send_notification(socket, false, "Interactions not allowed when setting gps coordinates", "");
+ send_notification(socket, false, "Back", "");
+ return ;
+ }
await spawnPromise("bash", ["/conf/back.sh"]);
});
socket.on("home", async () => {
+ if (gps_setting_in_progress) {
+ send_notification(socket, false, "Interactions not allowed when setting gps coordinates", "");
+ return ;
+ }
await spawnPromise("bash", ["/conf/home.sh"]);
});
socket.on("install", async () => {
const res = await spawnPromise("bash", ["/conf/install.sh"]);
send_notification(
socket,
res.code === 0,
"Installing the application",
res.output
);
});
// drag handles both drag and click
socket.on("motionevent", async (data) => {
+ if (gps_setting_in_progress) {
+ send_notification(socket, false, "Interactions not allowed when setting gps coordinates", "");
+ return ;
+ }
await spawnPromise("bash", [
"/conf/motionevent.sh",
data.motionType,
data.x + "",
data.y + ""
]);
});
// drag handles both drag and click
socket.on("drag", async (data) => {
+ if (gps_setting_in_progress) {
+ send_notification(socket, false, "Interactions not allowed when setting gps coordinates", "");
+ return ;
+ }
await spawnPromise("bash", [
"/conf/drag.sh",
data.startX + "",
data.startY + "",
data.endX + "",
data.endY + "",
data.dragTime + "",
]);
});
socket.on("key", async (data) => {
+ if (gps_setting_in_progress) {
+ send_notification(socket, false, "Interactions not allowed when setting gps coordinates", "");
+ return ;
+ }
await spawnPromise("bash", [
"/conf/press_key.sh",
data.key
]);
});
socket.on("setcoord", async (data) => {
+ if (gps_setting_in_progress) {
+ send_notification(socket, false, "Interactions not allowed when setting gps coordinates", "");
+ return ;
+ }
+ gps_setting_in_progress = true;
const res = await spawnPromise("bash", [
- "/conf/set_geo.sh",
+ "/conf/set_geo_full.sh",
data.lon + "",
data.lat + "",
]);
send_notification(
socket,
res.code === 0,
"Setting the moch location",
res.output
);
+ gps_setting_in_progress = false;
await send_private_data();
});
});
io.listen(3000);
console.log("listening on port 3000");
diff --git a/android/conf/docker-entrypoint.sh b/android/conf/docker-entrypoint.sh
index 68cc355..12996b6 100644
--- a/android/conf/docker-entrypoint.sh
+++ b/android/conf/docker-entrypoint.sh
@@ -1,6 +1,6 @@
-bash /conf/start_culebra.sh
+bash /conf/start_culebra.sh &
npm i -C /code
bash /conf/wait_for_sd.sh
node /code/index.mjs
#tail -f /dev/null
diff --git a/android/conf/get_adid.sh b/android/conf/get_adid.sh
new file mode 100644
index 0000000..896eceb
--- /dev/null
+++ b/android/conf/get_adid.sh
@@ -0,0 +1 @@
+adb shell su root cat /data/data/com.google.android.gms/shared_prefs/adid_settings.xml | xmllint --xpath 'string(//map/string[@name="adid_key"])' -
diff --git a/android/conf/reset_adid.sh b/android/conf/reset_adid.sh
index 9e01789..eea556f 100644
--- a/android/conf/reset_adid.sh
+++ b/android/conf/reset_adid.sh
@@ -1,13 +1,15 @@
#!/bin/bash
-set -xe
+set -x
# Change the file slightly
echo | adb shell su root "tee /data/data/com.google.android.gms/shared_prefs/adid_settings.xml"
# Ask android for the ADID. Android detects the changed file (somewhere), and regenerates it
adb shell su root am start -W -n com.example.adidreader/com.example.adidreader.MainActivity
-sleep 0.1
+(exit 1)
-# Get the new ADID
-adb shell su root cat /data/data/com.google.android.gms/shared_prefs/adid_settings.xml | xmllint --xpath 'string(//map/string[@name="adid_key"])' - > /adid
+while [ $? -ne 0 ]; do
+ # Get the new ADID
+ adb shell su root cat /data/data/com.google.android.gms/shared_prefs/adid_settings.xml | xmllint --xpath 'string(//map/string[@name="adid_key"])' - > /dev/null
+done
diff --git a/android/conf/set_geo.sh b/android/conf/set_geo.sh
index ab65c9d..bfb06b8 100644
--- a/android/conf/set_geo.sh
+++ b/android/conf/set_geo.sh
@@ -1,21 +1,32 @@
#!/bin/bash
-ko=$((
+error_msg=$((
echo "auth $(cat ~/.emulator_console_auth_token)"
echo "geo fix $1 $2 0.0"
sleep 0.2
) | telnet localhost 5554 2> /dev/null | grep KO: );
-if [ -z "${ko}" ]; then
+sleep 1
+# Technically the phone supports (prints) 6 digits of precision,
+# but the last one is subject to weird rounding, so just checking the first 5
+new_1=$(printf "%.5f" $1)
+new_2=$(printf "%.5f" $2)
+correct=${new_2},${new_1}
+
+if [ -z "${error_msg}" ]; then
# Check if the location was actually set, for some reason the location moching doesn't work,
# unless google maps is launched first, and is given access to the phone location
- adb shell dumpsys location | grep 'last location=Location\[gps' > /dev/null
- if [ $? -ne 0 ]; then
- echo Failed to actually set the location, probably need to launch google maps first
+ out=$(. /conf/get_location.sh);
+
+ curr_1=$(printf "%.5f" $(echo $out | sed 's/.*,//'))
+ curr_2=$(printf "%.5f" $(echo $out | sed 's/,.*//'))
+
+ if [[ "$curr_1" != "$new_1" || "$curr_2" != "$new_2" ]]; then
+ echo Failed to actually set the location, but detected a prev location set
exit 1
fi
- echo Succesfully set the coordinates to: $(. /conf/get_location.sh)
+ echo Succesfully set the coordinates to: $out
exit 0
else
- echo "$ko"
- exit 1;
+ echo "$error_msg"
+ exit 2;
fi
diff --git a/android/conf/set_geo_full.sh b/android/conf/set_geo_full.sh
new file mode 100644
index 0000000..e17fa3e
--- /dev/null
+++ b/android/conf/set_geo_full.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+last_stat=1;
+output="";
+function set_coords_checked {
+ output=$(bash /conf/set_geo.sh $1 $2)
+ last_stat=$?
+ if test $last_stat -eq 2; then
+ echo $output
+ exit 1
+ fi
+}
+
+count=0;
+while true; do
+ set_coords_checked $1 $2
+ if [ $last_stat -eq 0 -o $count -ge 3 ]; then
+ break;
+ fi
+
+ adb shell am start-activity com.google.android.apps.maps &> /dev/null
+
+ count_inner=0;
+ last_stat=1;
+ while [ $last_stat -ne 0 -a $count_inner -le 40 ]; do
+ set_coords_checked $1 $2;
+ count_inner=$(( $count_inner + 1 ));
+ done
+
+ adb shell am stop-app com.google.android.apps.maps
+ sleep 1
+ count=$(( $count + 1 ));
+done
+if [ $last_stat -eq 0 ]; then
+ echo $output
+else
+ echo Failed to change the coordinates, try again later
+fi
+exit $last_stat
diff --git a/android/conf/start_culebra.sh b/android/conf/start_culebra.sh
index 8473c21..98931e2 100644
--- a/android/conf/start_culebra.sh
+++ b/android/conf/start_culebra.sh
@@ -1,26 +1,18 @@
-set -xe
+set -x
+
+function culebra_loop() {
+ export PATH=$PATH:/root/culebraDependencies
+ cd /root/culebra
+ while true; do
+ ./culebratester2 start-server
+ done
+}
+
rm -f /opt/android-sdk-linux/.android/avd/virtual_dev.avd/*.lock
adb start-server
-cat > /simple.gpx << EOF
-<?xml version="1.0" encoding="UTF-8"?>
-<gpx version="1.1" creator="ChatGPT" xmlns="http://www.topografix.com/GPX/1/1">
- <wpt lat="37.422" lon="-122.084">
- <name>Simple Waypoint</name>
- </wpt>
-</gpx>
-EOF
emulator -avd virtual_dev -writable-system -no-window -no-audio &
adb wait-for-device
adb emu avd snapshot load configured
-# emulator -grpc 5556 -avd virtual_dev -snapshot configured -no-window -no-audio -no-metrics -debug-no > /dev/null &
-# adb wait-for-device
-
-export PATH=$PATH:/root/culebraDependencies
-cd /root/culebra
-./culebratester2 start-server &
-#wait for the server to start
-while ! curl http://localhost:9987/v2/uiDevice/screenshot > /dev/null 2> /dev/null; do
- sleep 0.1
-done
+culebra_loop
diff --git a/http_server/code/index.html b/http_server/code/index.html
index e46cfdb..a58cc3b 100644
--- a/http_server/code/index.html
+++ b/http_server/code/index.html
@@ -1,468 +1,474 @@
<!doctype html>
<html lang="en">
<head style="height: 100vh">
<meta charset="UTF-8" />
<title>Rentgen android</title>
<script src="/htmx.js"></script>
<style>
main {
display: flex;
}
.log-section {
height: auto;
width: 400px;
overflow: auto;
display: flex;
flex-direction: column;
margin-left: 20px;
}
.screen {
display: inline-block;
cursor: pointer;
}
.screen-buttons {
display: flex;
justify-content: space-around;
margin-top: 5px;
gap: 10px;
}
.screen-buttons button {
font-size: 1.1rem;
padding: 10px 20px;
width: 100%;
cursor: pointer;
background-color: transparent;
}
.screen-buttons button:hover {
background-color: aqua;
}
#clicks-log {
font-family:
Menlo,
Consolas,
Monaco,
Liberation Mono,
Lucida Console,
monospace;
}
.tab {
border: 1px solid #ccc;
background-color: #f1f1f1;
}
/* Style the buttons that are used to open the tab content */
.tab button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
}
/* Change background color of buttons on hover */
.tab button:hover {
background-color: #ddd;
}
/* Create an active/current tablink class */
.tab button.active {
background-color: #ccc;
}
.tabcontent.active {
display: flex;
flex-direction: column;
flex-grow: 1;
}
/* Style the tab content */
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}
html,
body,
main {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
}
main {
display: flex;
flex-direction: row;
align-items: stretch;
}
#logs-tab {
overflow: auto;
text-wrap: wrap;
}
.screen-section {
display: flex;
flex-direction: column;
}
.screen-section #screen,
.screen-section.screen_buttons {
flex-grow: 0;
}
#screen {
user-select: none;
}
.tab-section {
display: flex;
flex-direction: column;
flex-grow: 1;
}
#resp {
display: none;
}
#upload_form {
display: flex;
flex-direction: column;
}
#upload_form button,
#upload_form label {
border: 2px solid #ccc;
background-color: #f1f1f1;
cursor: pointer;
padding: 3px 10px;
transition: 0.3s;
}
#upload_form button:hover,
#upload_form label:hover {
background-color: #ddd;
}
#notifications {
width: 40%;
margin-left: 60%;
position: absolute;
}
+ #logs {
+ display: flex;
+ flex-direction: row;
+ }
</style>
</head>
<body>
<div id="notifications"></div>
<div id="resp" style="display: none"></div>
<main>
<section class="screen-section">
<img
id="screen"
alt="android screen"
src=""
draggable="false"
class="screen"
style="flex-grow: 0"
tabindex="0"
/>
<div class="screen-buttons" style="flex-grow: 0">
<button class="screen-buttons-home">home</button>
<button class="screen-buttons-back">back</button>
</div>
<form
id="upload_form"
hx-post="/upload_apk"
enctype="multipart/form-data"
hx-target="#resp"
>
<label id="upload_input" for="app"
>Select file
<input
type="file"
id="app"
name="app"
accept=".apk"
required
multiple
/>
</label>
<button type="submit">Install the app</button>
</form>
</section>
<div class="tab-section">
<div class="tab">
<button
class="tablinks active"
onclick="open_tab(event, 'httptoolkit-tab')"
>
HttpToolkit UI
</button>
<button class="tablinks" onclick="open_tab(event, 'logs-tab')">
Logs
</button>
<button class="tablinks" onclick="open_tab(event, 'controls-tab')">
Device Controls
</button>
</div>
<div class="tabcontent" id="logs-tab">
- <p id="clicks-log" class="log-section"></p>
- <p id="traffic-log" class="log-section"></p>
+ <div id="logs">
+ <p id="clicks-log" class="log-section"></p>
+ <p id="traffic-log" class="log-section"></p>
+ </div>
</div>
<div class="tabcontent active" id="httptoolkit-tab">
<iframe
id="httptoolkit-frame"
style="flex-grow: 1"
src="http://localhost:9080/"
title="httptoolkit"
></iframe>
</div>
<div class="tabcontent" id="controls-tab">
<form id="set_coords" onsubmit="coords_handler(event)">
<label>
Latitude:
<input type="text" name="lat" />
</label>
<label>
Longitude:
<input type="text" name="lon" />
</label>
<button type="submit">Submit coords</button>
</form>
<button id="reset_adid_btn" onclick="reset_adid_handler(event)">Reset ADID</button>
<table>
<thead></thead>
<tbody>
<tr>
<td>ADID:</td>
<td id="adid_priv_info_table">UNKNOWN</td>
</tr>
<tr>
<td>Longitude:</td>
<td id="lon_priv_info_table">UNKNOWN</td>
</tr>
<tr>
<td>Latitude:</td>
<td id="lat_priv_info_table">UNKNOWN</td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
<script src="/socket.io.js"></script>
<script>
var socket = io();
function reset_adid_handler(e) {
socket.emit("reset_adid");
}
function coords_handler(e) {
e.preventDefault();
const form_data = new FormData(e.target);
console.log(form_data);
socket.emit("setcoord", {
lon: Number.parseFloat(form_data.get("lon")),
lat: Number.parseFloat(form_data.get("lat")),
});
}
function open_tab(evt, tab_name) {
let i, tabcontent, tablinks;
// Get all elements with class="tabcontent" and hide them
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
if (tabcontent[i].id != tab_name) {
tabcontent[i].classList.remove("active");
} else {
tabcontent[i].classList.add("active");
}
}
// Get all elements with class="tablinks" and remove the class "active"
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].classList.remove("active");
}
// Show the current tab, and add an "active" class to the button that opened the tab
evt.currentTarget.classList.add("active");
}
var screen = document.getElementById("screen");
var clicksLog = document.getElementById("clicks-log");
const homeButton = document.querySelector(".screen-buttons-home");
const backButton = document.querySelector(".screen-buttons-back");
let lastTouch = new Date().getTime();
const calculateElapsedTime = (last) => {
const currentTouch = new Date().getTime();
const elapsedTime = currentTouch - lastTouch;
const elapsedSec = Math.round(elapsedTime / 1000);
lastTouch = currentTouch;
return elapsedSec;
};
const waitToLog = (clickInfoText) => {
const clickInfo = document.createElement("span");
const waitInfo = document.createElement("span");
waitInfo.textContent = `await wait(${calculateElapsedTime(
lastTouch
)});`;
clicksLog.appendChild(waitInfo);
clickInfo.textContent = clickInfoText;
clicksLog.appendChild(clickInfo);
};
const registerClick = ({ path, logText, body }) => {
const clicksLog = document.getElementById("clicks-log");
const span = document.createElement("span");
waitToLog(logText);
socket.emit(path, body ? body : {});
};
homeButton.addEventListener("click", () =>
registerClick({ path: "home", logText: "await homeButton();" })
);
backButton.addEventListener("click", () =>
registerClick({ path: "back", logText: "await backButton();" })
);
socket.on("screenshot_data", (data) => {
try {
const blob = new Blob([data]);
screen.src = URL.createObjectURL(blob);
} catch (error) {
console.error("Error fetching image: ", error);
}
});
socket.on("private_info", (data) => {
console.log("private_info");
adid_priv_info_table.textContent = data.adid;
lat_priv_info_table.textContent = data.latitude;
lon_priv_info_table.textContent = data.longitude;
});
socket.emit("private_info_req");
socket.onAny((ev, ...args) => {
console.log("ev: ", ev, args);
});
async function displayImage() {
socket.emit("screenshot");
}
let isDragging = false;
const screenSize = [320, 640];
function calcMousePos(event) {
let rect = screen.getBoundingClientRect();
let x = ((event.clientX - rect.left) / rect.width) * screenSize[0];
let y = ((event.clientY - rect.top) / rect.height) * screenSize[1];
x = Math.min(Math.max(x, 0), screenSize[0]);
y = Math.min(Math.max(y, 0), screenSize[1]);
return { x, y };
}
screen.addEventListener(
"mousemove",
(event) => {
if (!isDragging) return;
let pos = calcMousePos(window.event);
if (isDragging) {
registerClick({
path: "motionevent",
logText: `await motionevent({motionType: "MOVE", x:${pos.x},y:${pos.y}}});`,
body: {
motionType: "MOVE",
x: pos.x,
y: pos.y,
},
});
}
},
false
);
const handleDraggStart = (event) => {
isDragging = true;
let pos = calcMousePos(event);
registerClick({
path: "motionevent",
logText: `await motionevent({motionType: "DOWN", x:${pos.x},y:${pos.y}}});`,
body: {
motionType: "DOWN",
x: pos.x,
y: pos.y,
},
});
};
screen.addEventListener("mousedown", handleDraggStart);
document.addEventListener("mouseup", (e) => {
if (!isDragging) return;
isDragging = false;
let pos = calcMousePos(e);
registerClick({
path: "motionevent",
logText: `await motionevent({motionType: "MOVE", x:${pos.x},y:${pos.y}}});`,
body: {
motionType: "MOVE",
x: pos.x,
y: pos.y,
},
});
registerClick({
path: "motionevent",
logText: `await motionevent({motionType: "UP", x:${pos.x},y:${pos.y}}});`,
body: {
motionType: "UP",
x: pos.x,
y: pos.y,
},
});
});
window.addEventListener("keydown", (event) => {
let key = event.key;
if (key === "Space") key = " ";
else if (key !== "Enter" && key !== "Backspace" && key.length !== 1)
return;
console.log(event.key, key);
if (document.getElementById("screen").matches(":hover")) {
registerClick({
path: "key",
logText: `await key(${event.key});`,
body: { key },
});
}
});
async function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
async function screenshot_loop() {
var before;
while (true) {
before = performance.now();
await displayImage();
while (performance.now() - before < ___screenshotDelayMs___)
await sleep(50);
}
}
screenshot_loop();
</script>
<script src="/trafficLog.js"></script>
<script src="/notifications.js"></script>
</body>
</html>
diff --git a/http_server/code/index.mjs b/http_server/code/index.mjs
index 00ed30c..a60e24e 100644
--- a/http_server/code/index.mjs
+++ b/http_server/code/index.mjs
@@ -1,96 +1,111 @@
import express from "express";
import { readFile } from "node:fs/promises";
import { execSync } from "node:child_process";
import { Server } from "socket.io";
import { io } from "socket.io-client";
+import { build_html } from "./har-analyzer/build_html.js"
import fileUpload from "express-fileupload";
const app = express();
async function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
console.log("Waiting for full boot...");
// bidirectional forwarding
// Wait untill the connection
const back = io("ws://android:3000", {
reconnectionAttempts: 1000000,
reconnectionDelay: 100,
});
while (!back.connected) {
await sleep(100);
}
console.log("Boot detected! activating endpoints");
app.use(express.urlencoded({ extended: false }));
app.use(express.static("/code/dist"));
//GET
-app.get("/favicon.ico", function (req, res) {
+app.get("/favicon.ico", function (_req, res) {
res.sendFile("/code/favicon.ico");
});
-app.get("/htmx.js", function (req, res) {
+app.get("/htmx.js", function (_req, res) {
res.sendFile("/code/node_modules/htmx.org/dist/htmx.min.js");
});
app.get("/socket.io.js", function (_req, res) {
res.sendFile("/code/node_modules/socket.io/client-dist/socket.io.js");
});
app.get("/", async function (req, res) {
let fileData = (await readFile("/code/index.html")).toString();
fileData = fileData.replace(
"___screenshotDelayMs___",
process.env.screenshotDelayMs
);
res.setHeader("Content-Type", "text/html");
res.setHeader("Content-Disposition", "inline");
res.send(fileData);
});
//POST
+app.use(express.text({limit: "100mb"}));
+
+app.post("/inspect_har", function (req, res) {
+ let body = JSON.parse(req.body);
+ let har = JSON.stringify(body.har);
+ console.log(har);
+ let private_data;
+ if (body.private_data)
+ private_data = JSON.stringify(body.private_data);
+ res.setHeader("Content-Type", "text/html");
+ res.send(build_html(har, private_data, "/code/har-analyzer/"));
+});
+
app.use(fileUpload());
app.post("/upload_apk", async function (req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send("No files were uploaded.");
}
execSync("rm -rf /shared_buffer/*");
if (Array.isArray(req.files.app)) {
for (const [idx, file] of req.files.app.entries()) {
let uploadPath = "/shared_buffer/app" + idx + ".apk";
await file.mv(uploadPath);
}
} else {
let uploadPath = "/shared_buffer/app" + 0 + ".apk";
await req.files.app.mv(uploadPath);
}
back.emit("install");
res.send("Files uploaded!");
});
+
let server = app.listen(8080, () => console.log("Listening in port 8080"));
const front = new Server(server);
// forwarding the messages
front.on("connection", (socket) => {
socket.onAny((event, ...args) => {
if (back.connected) {
back.emit(event, ...args);
} else {
console.log("Front tried to send: ", event, ...args);
}
});
});
back.onAny((event, ...args) => {
front.emit(event, ...args);
});
diff --git a/http_server/code/package-lock.json b/http_server/code/package-lock.json
index b57ca33..bd86331 100644
--- a/http_server/code/package-lock.json
+++ b/http_server/code/package-lock.json
@@ -1,1441 +1,1448 @@
{
"name": "code",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
+ "@types/har-format": "^1.2.16",
"express": "^4.18.2",
"express-fileupload": "^1.5.1",
"htmx.org": "^1.9.12",
"preact": "^10.18.1",
"socket.io": "^4.8.1",
"socket.io-client": "^4.8.1",
"ws": "^8.18.0"
},
"devDependencies": {
"esbuild": "^0.19.5"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz",
"integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz",
"integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz",
"integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz",
"integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz",
"integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz",
"integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz",
"integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz",
"integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz",
"integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz",
"integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz",
"integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz",
"integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz",
"integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz",
"integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz",
"integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz",
"integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz",
"integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz",
"integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz",
"integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz",
"integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz",
"integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz",
"integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"license": "MIT"
},
"node_modules/@types/cors": {
"version": "2.8.18",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.18.tgz",
"integrity": "sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
+ "node_modules/@types/har-format": {
+ "version": "1.2.16",
+ "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
+ "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==",
+ "license": "MIT"
+ },
"node_modules/@types/node": {
"version": "22.15.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
"license": "MIT",
"engines": {
"node": "^4.5.0 || >= 5.9"
}
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/engine.io": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
"integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
"license": "MIT",
"dependencies": {
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.7.2",
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1"
},
"engines": {
"node": ">=10.2.0"
}
},
"node_modules/engine.io-client": {
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-client/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/engine.io-client/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/engine.io/node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/engine.io/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/engine.io/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/engine.io/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/esbuild": {
"version": "0.19.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz",
"integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.19.5",
"@esbuild/android-arm64": "0.19.5",
"@esbuild/android-x64": "0.19.5",
"@esbuild/darwin-arm64": "0.19.5",
"@esbuild/darwin-x64": "0.19.5",
"@esbuild/freebsd-arm64": "0.19.5",
"@esbuild/freebsd-x64": "0.19.5",
"@esbuild/linux-arm": "0.19.5",
"@esbuild/linux-arm64": "0.19.5",
"@esbuild/linux-ia32": "0.19.5",
"@esbuild/linux-loong64": "0.19.5",
"@esbuild/linux-mips64el": "0.19.5",
"@esbuild/linux-ppc64": "0.19.5",
"@esbuild/linux-riscv64": "0.19.5",
"@esbuild/linux-s390x": "0.19.5",
"@esbuild/linux-x64": "0.19.5",
"@esbuild/netbsd-x64": "0.19.5",
"@esbuild/openbsd-x64": "0.19.5",
"@esbuild/sunos-x64": "0.19.5",
"@esbuild/win32-arm64": "0.19.5",
"@esbuild/win32-ia32": "0.19.5",
"@esbuild/win32-x64": "0.19.5"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/express-fileupload": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.5.1.tgz",
"integrity": "sha512-LsYG1ALXEB7vlmjuSw8ABeOctMp8a31aUC5ZF55zuz7O2jLFnmJYrCv10py357ky48aEoBQ/9bVXgFynjvaPmA==",
"license": "MIT",
"dependencies": {
"busboy": "^1.6.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/htmx.org": {
"version": "1.9.12",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz",
"integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw==",
"license": "0BSD"
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/preact": {
"version": "10.18.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.18.1.tgz",
"integrity": "sha512-mKUD7RRkQQM6s7Rkmi7IFkoEHjuFqRQUaXamO61E6Nn7vqF/bo7EZCmSyrUnp2UWHw0O7XjZ2eeXis+m7tf4lg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/socket.io": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
"integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"cors": "~2.8.5",
"debug": "~4.3.2",
"engine.io": "~6.6.0",
"socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.2.0"
}
},
"node_modules/socket.io-adapter": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
"license": "MIT",
"dependencies": {
"debug": "~4.3.4",
"ws": "~8.17.1"
}
},
"node_modules/socket.io-adapter/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io-adapter/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/socket.io-adapter/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/socket.io-client": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-client/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io-client/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io-parser/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/socket.io/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
"engines": {
"node": ">=0.4.0"
}
}
}
}
diff --git a/http_server/code/package.json b/http_server/code/package.json
index a6176a7..4ea4dce 100644
--- a/http_server/code/package.json
+++ b/http_server/code/package.json
@@ -1,17 +1,18 @@
{
"scripts": {
- "build": "esbuild --sourcemap --bundle src/trafficLog.jsx src/notifications.jsx --outdir=dist/ --jsx-factory=h --jsx-fragment=Fragment"
+ "build": "esbuild --sourcemap --bundle src/trafficLog.tsx src/notifications.jsx --outdir=dist/ --jsx-factory=h --jsx-fragment=Fragment"
},
"dependencies": {
+ "@types/har-format": "^1.2.16",
"express": "^4.18.2",
"express-fileupload": "^1.5.1",
"htmx.org": "^1.9.12",
"preact": "^10.18.1",
"socket.io": "^4.8.1",
"socket.io-client": "^4.8.1",
"ws": "^8.18.0"
},
"devDependencies": {
"esbuild": "^0.19.5"
}
}
diff --git a/http_server/code/screenshot.mjs b/http_server/code/screenshot.mjs
deleted file mode 100644
index 4caac92..0000000
--- a/http_server/code/screenshot.mjs
+++ /dev/null
@@ -1,68 +0,0 @@
-import fs from "fs";
-import { sleep } from "./utils.mjs";
-import { WebSocket } from "ws";
-
-export const android_websocket = new WebSocket("ws://android:3000");
-
-let doneWrite = 0;
-let screenshotPromise = null;
-
-async function screenshot() {
- const time_start = Date.now();
- let retries = 0;
- while (android_websocket.readyState != WebSocket.OPEN) {
- await sleep(15);
- retries++;
- if (retries > 50) {
- console.error("Screenshot ws timeout");
- doneWrite = 0;
- screenshotPromise = null;
- return;
- }
- }
- android_websocket.send("screenshot");
- while (!doneWrite) {
- await sleep(15);
- if (Date.now() - time_start > 2000) {
- console.error("Screenshot timed out after 2s");
- break; // timeout
- }
- }
- doneWrite = 0;
- screenshotPromise = null;
-}
-
-export async function guardedScreenshot() {
- if (!screenshotPromise) {
- screenshotPromise = screenshot();
- } else {
- console.log("ongoing screenshot promise not taking a new one");
- }
- return screenshotPromise;
-}
-
-export async function waitFullBoot() {
- var start = performance.now();
- var counter = 0;
-
- //will timeout after 10 min
- while (performance.now() - start < 600 * 1000) {
- var before = performance.now();
- await screenshot();
- var after = performance.now();
- if (after - before < process.env.screenshotDelayMs) counter++;
- else counter = 0;
-
- if (counter === 10) return;
- }
-
- throw new Error("wait for screenshot time to be less than 0.5s timed out");
-}
-
-android_websocket.on("message", (dataBuf) => {
- let fd;
- fd = fs.openSync("/code/screenshot.png", "w");
- fs.writeSync(fd, dataBuf);
- fs.close(fd);
- doneWrite = 1;
-});
diff --git a/http_server/code/src/trafficLog.jsx b/http_server/code/src/trafficLog.jsx
deleted file mode 100644
index e9b3297..0000000
--- a/http_server/code/src/trafficLog.jsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import { h, render, Component } from "preact";
-
-/**
- * @typedef {'urlParam' | 'body' | 'header'} Source;
- */
-
-/**
- * @typedef {{
- * body: string,
- * headers: [[string, string]],
- * url: URL,
- * id: string,
- * kv: Map<[Source, string], string>,
- * }} Req
- */
-
-/**
- * @returns {Req | null}
- */
-function process_msg(s) {
- let obj = JSON.parse(s);
- if (obj.type !== "data") return;
- if (!obj.payload || !obj.payload.data || !obj.payload.data.requestReceived)
- return null;
- let req = obj.payload.data.requestReceived;
- if (!req.rawHeaders || !req.body || !req.url) return null;
-
- /**
- *@type {Map<[Source, string], string>}
- */
- let kv = new Map();
- /**
- *@type {string}
- */
- let body = req.body;
- /**
- *@type {string}
- */
- let decoded_body;
- try {
- decoded_body = atob(body);
- } catch {
- decoded_body = "";
- }
-
- kv.set(["body", "raw"], decoded_body);
-
- /**
- *@type {string}
- */
- let url = new URL(req.url);
-
- const params = new URLSearchParams(url.search);
-
- for (const [k, v] of params.entries()) {
- kv.set(["urlParam", k], v);
- }
-
- /**
- *@type {[[string, string]]}
- */
- let headers = JSON.parse(req.rawHeaders);
-
- for (const [k, v] of headers) {
- kv.set(["header", k], v);
- }
-
- /**
- *@type {[[string, string]]}
- */
- let id = req.id;
-
- /**
- *@type Req
- */
- let ret_req = { id, body: decoded_body, url, headers, kv };
- return ret_req;
-}
-
-class TrafficLog extends Component {
- constructor() {
- super();
- /**
- * @type {{requests: [Req]}}
- */
- this.state = { requests: [] };
- }
-
- componentDidMount() {
- // This should also be dynamic
- this.connection = new WebSocket("ws://localhost:10001");
- this.connection.onmessage = (msg) => {
- let req = process_msg(msg.data);
- if (req) {
- this.setState({ requests: [...this.state.requests, req] });
- }
- };
- this.connection.onclose = this.connection.onerror = () => {
- var element = document.getElementById("httptoolkit-frame");
- element.parentNode.removeChild(element);
- };
- }
-
- componentWillUnmount() {
- clearInterval(this.intervalSetter);
- }
-
- render() {
- const contentWithLineBreaks = this.state.requests.map(
- (
- /**
- * @type {Req}
- */
- req
- ) => {
- return (
- <span>
- {req.url.href}
- <br />
- </span>
- );
- }
- );
-
- return <span>{contentWithLineBreaks}</span>;
- }
-}
-
-render(<TrafficLog />, document.getElementById("traffic-log"));
diff --git a/http_server/code/src/trafficLog.tsx b/http_server/code/src/trafficLog.tsx
new file mode 100644
index 0000000..c90b3ba
--- /dev/null
+++ b/http_server/code/src/trafficLog.tsx
@@ -0,0 +1,237 @@
+import { Entry, Har, PostData, Request, Response } from "har-format";
+import { render, Component } from "preact";
+
+type MyState = {
+ finished_entries: Entry[];
+ unfinished_entries: Map<string, Entry>;
+};
+
+class TrafficLog extends Component {
+ connection: WebSocket | undefined;
+
+ state: MyState = { finished_entries: [], unfinished_entries: new Map() };
+
+ constructor() {
+ super();
+ }
+
+ componentDidMount() {
+ // This should also be dynamic
+ this.connection = new WebSocket("ws://localhost:10001");
+ this.connection.onmessage = (msg) => {
+ this.process_msg(msg.data);
+ this.setState({
+ finished_entries: this.state.finished_entries,
+ unfinished_entries: this.state.unfinished_entries,
+ });
+ };
+ this.connection.onclose = this.connection.onerror = () => {
+ window.location.reload();
+ };
+ }
+
+ render() {
+ const download_har = () => {
+ var tempLink = document.createElement("a");
+ var taBlob = new Blob([JSON.stringify(this.export_har())], {
+ type: "text/plain",
+ });
+
+ tempLink.setAttribute("href", URL.createObjectURL(taBlob));
+ tempLink.setAttribute("download", `rentgendroid-capture.har`);
+
+ tempLink.click();
+
+ URL.revokeObjectURL(tempLink.href);
+ };
+
+ const inspect_har = async () => {
+ const req_body = {
+ har: this.export_har(),
+ private_data: undefined,
+ };
+ const resp = await fetch("/inspect_har", {
+ method: "POST",
+ body: JSON.stringify(req_body),
+ })
+ const resp_text = await resp.text();
+ const newWindow = window.open();
+ newWindow?.document.write(resp_text);
+ newWindow?.document.close();
+
+ }
+
+ const contentWithLineBreaks = this.state.finished_entries.map((req) => {
+ return (
+ <span>
+ {req.request.url}
+ <br />
+ </span>
+ );
+ });
+
+ return (
+ <div>
+ <button onClick={download_har}>Download HAR</button>
+ <button onClick={inspect_har}>Inspect HAR</button>
+ <div>
+ <h2>stats: </h2>
+ <p>
+ Request + responce pairs:{" "}
+ {this.state.finished_entries.length}
+ </p>
+ <p>
+ Waiting for the responce:{" "}
+ {this.state.unfinished_entries.size}
+ </p>
+ </div>
+ <div>{contentWithLineBreaks}</div>
+ </div>
+ );
+ }
+
+ process_msg(s: string) {
+ let obj = JSON.parse(s);
+ console.log(obj);
+
+ if (obj.type !== "data") return;
+ if (obj.payload && obj.payload.data && obj.payload.data.requestReceived)
+ this.process_req(obj.payload.data.requestReceived);
+ if (
+ obj.payload &&
+ obj.payload.data &&
+ obj.payload.data.responseCompleted
+ )
+ this.process_res(obj.payload.data.responseCompleted);
+ }
+
+ process_res(res: any) {
+ let entry = this.state.unfinished_entries.get(res.id)!;
+
+ let content_type = "application/text";
+ let headers = JSON.parse(res.rawHeaders).map(
+ (header: [string, string]) => {
+ if (header[0].toLowerCase() === "content-type")
+ content_type = header[1];
+ return { name: header[0], value: header[1], comment: "" };
+ }
+ );
+
+ //'{"startTime":1751745139334,
+ // "startTimestamp":347666.762487,
+ // "bodyReceivedTimestamp":347667.529477,
+ // "headersSentTimestamp":347906.038202,
+ // "responseSentTimestamp":347906.616067}'
+
+ let timing_events = JSON.parse(res.timingEvents);
+
+ let start_ts = timing_events.startTimestamp;
+ let got_headers_ts = timing_events.headersSentTimestamp;
+ let end_ts = timing_events.responseSentTimestamp;
+
+ let wait_time = got_headers_ts - start_ts;
+ let recieve_time = end_ts - got_headers_ts;
+
+ let response: Response = {
+ status: res.statusCode,
+ statusText: res.statusMessage,
+ httpVersion: entry.request.httpVersion,
+ cookies: [],
+ headers,
+ content: {
+ size: 0,
+ mimeType: content_type,
+ text: res.body,
+ encoding: "base64",
+ },
+ redirectURL: "",
+ headersSize: -1,
+ bodySize: -1,
+ };
+
+ entry.response = response;
+ entry.timings.wait = wait_time;
+ entry.timings.receive = recieve_time;
+ this.state.unfinished_entries.delete(res.id);
+ this.state.finished_entries.push(entry);
+ }
+
+ process_req(req: any) {
+ let content_type = "application/text";
+
+ let headers = JSON.parse(req.rawHeaders).map(
+ (header: [string, string]) => {
+ if (header[0].toLowerCase() === "Content-Type")
+ content_type = header[1];
+ return { name: header[0], value: header[1], comment: "" };
+ }
+ );
+
+ let timing_events = JSON.parse(req.timingEvents);
+
+ let start_time: number = timing_events.startTime!;
+
+ let start_datetime = new Date(start_time).toISOString();
+
+ let request: Request = {
+ method: req.method,
+ url: req.url,
+ httpVersion: req.httpVersion,
+ cookies: [],
+ headers,
+ queryString: [],
+ postData: req.body
+ ? ({ text: req.body, mimeType: content_type } as PostData)
+ : undefined,
+ headersSize: -1,
+ bodySize: -1,
+ comment: "",
+ };
+
+ //'{"startTime":1751745139334,"startTimestamp":347666.762487,"bodyReceivedTimestamp":347667.529477,"headersSentTimestamp":347906.038202,"responseSentTimestamp":347906.616067}'
+ let entry: Entry = {
+ startedDateTime: start_datetime,
+ time: 0,
+ request: request,
+ response: {
+ status: 0,
+ statusText: "",
+ httpVersion: "",
+ cookies: [],
+ headers: [],
+ content: {
+ size: 0,
+ mimeType: "",
+ },
+ redirectURL: "",
+ headersSize: 0,
+ bodySize: 0,
+ },
+ cache: {},
+ timings: {
+ wait: 0,
+ receive: 0,
+ },
+ };
+ this.state.unfinished_entries.set(req.id, entry);
+ }
+
+ export_har(): Har {
+ let ret: Har = {
+ log: {
+ version: "1.2",
+ creator: {
+ name: "Rentgendroid",
+ version: "0.0.1",
+ },
+ entries: [
+ ...this.state.finished_entries,
+ ...this.state.unfinished_entries.values(),
+ ],
+ },
+ };
+ return ret;
+ }
+}
+
+render(<TrafficLog />, document.getElementById("traffic-log")!);
diff --git a/pre_android/Dockerfile b/pre_android/Dockerfile
index 5520964..ccc4972 100644
--- a/pre_android/Dockerfile
+++ b/pre_android/Dockerfile
@@ -1,9 +1,9 @@
FROM runmymind/docker-android-sdk:ubuntu-standalone-20230511
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/android-sdk-linux/cmdline-tools/latest/bin:/opt/android-sdk-linux/cmdline-tools/tools/bin:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/build-tools/32.0.0:/opt/android-sdk-linux/platform-tools:/opt/android-sdk-linux/emulator:/opt/android-sdk-linux/bin
RUN sdkmanager --channel=2 "system-images;android-35;google_apis;x86_64" \
&& echo no | avdmanager create avd -n virtual_dev -b google_apis/x86_64 -k "system-images;android-35;google_apis;x86_64" \
- && apt-get update && apt-get install -y iproute2 iputils-ping npm git libxml2-utils telnet
+ && apt-get update && apt-get install -y iproute2 iputils-ping npm git libxml2-utils telnet bc
CMD bash /preconf/docker-entrypoint.sh
diff --git a/pre_android/preconf/AdIdreader/.gitignore b/pre_android/preconf/AdIdreader/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/pre_android/preconf/AdIdreader/.idea/.gitignore b/pre_android/preconf/AdIdreader/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/pre_android/preconf/AdIdreader/.idea/AndroidProjectSystem.xml b/pre_android/preconf/AdIdreader/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="AndroidProjectSystem">
+ <option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/compiler.xml b/pre_android/preconf/AdIdreader/.idea/compiler.xml
new file mode 100644
index 0000000..b86273d
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/compiler.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <bytecodeTargetLevel target="21" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/deploymentTargetSelector.xml b/pre_android/preconf/AdIdreader/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..b268ef3
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="deploymentTargetSelector">
+ <selectionStates>
+ <SelectionState runConfigName="app">
+ <option name="selectionMode" value="DROPDOWN" />
+ </SelectionState>
+ </selectionStates>
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/gradle.xml b/pre_android/preconf/AdIdreader/.idea/gradle.xml
new file mode 100644
index 0000000..639c779
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/gradle.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleMigrationSettings" migrationVersion="1" />
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="testRunner" value="CHOOSE_PER_TEST" />
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/app" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/kotlinc.xml b/pre_android/preconf/AdIdreader/.idea/kotlinc.xml
new file mode 100644
index 0000000..c224ad5
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="KotlinJpsPluginSettings">
+ <option name="version" value="2.0.21" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/migrations.xml b/pre_android/preconf/AdIdreader/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/migrations.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectMigrations">
+ <option name="MigrateToGradleLocalJavaHome">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ </set>
+ </option>
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/misc.xml b/pre_android/preconf/AdIdreader/.idea/misc.xml
new file mode 100644
index 0000000..74dd639
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/misc.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ExternalStorageConfigurationManager" enabled="true" />
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/build/classes" />
+ </component>
+ <component name="ProjectType">
+ <option name="id" value="Android" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/runConfigurations.xml b/pre_android/preconf/AdIdreader/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="RunConfigurationProducerService">
+ <option name="ignoredProducers">
+ <set>
+ <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
+ <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
+ <option value="com.intellij.execution.junit.PatternConfigurationProducer" />
+ <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
+ <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
+ <option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
+ <option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
+ <option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
+ </set>
+ </option>
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/.idea/vcs.xml b/pre_android/preconf/AdIdreader/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/.gitignore b/pre_android/preconf/AdIdreader/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/build.gradle.kts b/pre_android/preconf/AdIdreader/app/build.gradle.kts
new file mode 100644
index 0000000..9e13e25
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/build.gradle.kts
@@ -0,0 +1,47 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ namespace = "com.example.adidreader"
+ compileSdk = 35
+
+ defaultConfig {
+ applicationId = "com.example.adidreader"
+ minSdk = 30
+ targetSdk = 35
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+}
+
+dependencies {
+
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.material)
+ implementation(libs.play.services.ads.identifier)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+}
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/proguard-rules.pro b/pre_android/preconf/AdIdreader/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/androidTest/java/com/example/adidreader/ExampleInstrumentedTest.kt b/pre_android/preconf/AdIdreader/app/src/androidTest/java/com/example/adidreader/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..1b40336
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/androidTest/java/com/example/adidreader/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.adidreader
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.adidreader", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/AndroidManifest.xml b/pre_android/preconf/AdIdreader/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cc3fa9e
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AdIdreader"
+
+ tools:targetApi="31">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.NoDisplay">
+ </activity>
+ </application>
+
+
+</manifest>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/java/com/example/adidreader/MainActivity.kt b/pre_android/preconf/AdIdreader/app/src/main/java/com/example/adidreader/MainActivity.kt
new file mode 100644
index 0000000..ecea878
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/java/com/example/adidreader/MainActivity.kt
@@ -0,0 +1,31 @@
+package com.example.adidreader
+import android.os.Bundle
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.appcompat.app.AppCompatActivity
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import com.google.android.gms.ads.identifier.AdvertisingIdClient
+
+
+class MainActivity : AppCompatActivity() {
+ private fun getAdId() {
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(applicationContext)
+ val adId = adInfo.id
+ println("ADID: $adId")
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 6)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ this.getAdId()
+ finish()
+ }
+}
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/drawable/ic_launcher_background.xml b/pre_android/preconf/AdIdreader/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/drawable/ic_launcher_foreground.xml b/pre_android/preconf/AdIdreader/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/pre_android/preconf/AdIdreader/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/values-night/themes.xml b/pre_android/preconf/AdIdreader/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..4fb8722
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.AdIdreader" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_200</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/black</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_200</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/values/colors.xml b/pre_android/preconf/AdIdreader/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_200">#FFBB86FC</color>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/values/strings.xml b/pre_android/preconf/AdIdreader/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..dfccd06
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">AdIdreader</string>
+</resources>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/values/themes.xml b/pre_android/preconf/AdIdreader/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..dc6e1b8
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.AdIdreader" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_500</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/white</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_700</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/xml/backup_rules.xml b/pre_android/preconf/AdIdreader/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..4df9255
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older than API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+ <!--
+ <include domain="sharedpref" path="."/>
+ <exclude domain="sharedpref" path="device.xml"/>
+-->
+</full-backup-content>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/app/src/main/res/xml/data_extraction_rules.xml b/pre_android/preconf/AdIdreader/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ <!-- TODO: Use <include> and <exclude> to control what is backed up.
+ <include .../>
+ <exclude .../>
+ -->
+ </cloud-backup>
+ <!--
+ <device-transfer>
+ <include .../>
+ <exclude .../>
+ </device-transfer>
+ -->
+</data-extraction-rules>
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/build.gradle.kts b/pre_android/preconf/AdIdreader/build.gradle.kts
new file mode 100644
index 0000000..922f551
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/build.gradle.kts
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+}
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/gradle.properties b/pre_android/preconf/AdIdreader/gradle.properties
new file mode 100644
index 0000000..20e2a01
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/pre_android/preconf/AdIdreader/gradle/libs.versions.toml b/pre_android/preconf/AdIdreader/gradle/libs.versions.toml
new file mode 100644
index 0000000..821a527
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/gradle/libs.versions.toml
@@ -0,0 +1,24 @@
+[versions]
+agp = "8.9.1"
+kotlin = "2.0.21"
+coreKtx = "1.16.0"
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+appcompat = "1.7.0"
+material = "1.12.0"
+playServicesAdsIdentifier = "18.2.0"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+play-services-ads-identifier = { group = "com.google.android.gms", name = "play-services-ads-identifier", version.ref = "playServicesAdsIdentifier" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+
diff --git a/pre_android/preconf/AdIdreader/gradle/wrapper/gradle-wrapper.jar b/pre_android/preconf/AdIdreader/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/pre_android/preconf/AdIdreader/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/pre_android/preconf/AdIdreader/gradle/wrapper/gradle-wrapper.properties b/pre_android/preconf/AdIdreader/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..e03905a
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Jun 02 19:17:28 CEST 2025
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/pre_android/preconf/AdIdreader/gradlew b/pre_android/preconf/AdIdreader/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/pre_android/preconf/AdIdreader/gradlew.bat b/pre_android/preconf/AdIdreader/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/pre_android/preconf/AdIdreader/settings.gradle.kts b/pre_android/preconf/AdIdreader/settings.gradle.kts
new file mode 100644
index 0000000..c3fa471
--- /dev/null
+++ b/pre_android/preconf/AdIdreader/settings.gradle.kts
@@ -0,0 +1,24 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "AdIdreader"
+include(":app")
+
\ No newline at end of file
diff --git a/pre_android/preconf/docker-entrypoint.sh b/pre_android/preconf/docker-entrypoint.sh
index a56a68d..8b20b68 100644
--- a/pre_android/preconf/docker-entrypoint.sh
+++ b/pre_android/preconf/docker-entrypoint.sh
@@ -1,14 +1,23 @@
adb start-server
emulator -avd virtual_dev -writable-system -no-window -no-audio &
+bash /preconf/install_adidreader.sh
bash /preconf/install_culebra.sh
-# Save the ad id while the emulator is still up
-su root cat /data/data/com.google.android.gms/shared_prefs/adid_settings.xml | xmllint --xpath 'string(//map/string[@name="adid_key"])' - > /adid
+# Open google maps once, skip all the screens, and give it gps permissions
+adb shell am start-activity com.google.android.apps.maps
+sleep 8
+adb shell input tap 268 49
+sleep 4
+adb shell input tap 290 323
+sleep 6
+adb shell input tap 147 413
+sleep 10
+adb shell am stop-app com.google.android.apps.maps
adb emu avd snapshot save configured
adb emu kill
#to let the host know it finished installing
install -m 777 /dev/null /preconf/finished
tail -f /dev/null
diff --git a/pre_android/preconf/install_adidreader.sh b/pre_android/preconf/install_adidreader.sh
new file mode 100644
index 0000000..4a563dc
--- /dev/null
+++ b/pre_android/preconf/install_adidreader.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -xe
+
+cd /preconf/AdIdreader
+./gradlew installDebug
diff --git a/pre_android/preconf/install_culebra.sh b/pre_android/preconf/install_culebra.sh
index fde18d5..f2bb0bf 100644
--- a/pre_android/preconf/install_culebra.sh
+++ b/pre_android/preconf/install_culebra.sh
@@ -1,10 +1,11 @@
adb wait-for-device
cd /root
git clone https://github.com/dtmilano/CulebraTester2-public culebra
git clone https://gist.github.com/dtmilano/4537110 culebraDependencies
export PATH=$PATH:/root/culebraDependencies
cd culebra
git checkout 4ce1987e7ec6ae627d8f33a1a3b59f684aff90c0
echo "/opt/android-sdk-linux" >> local.properties
-./culebratester2 install
\ No newline at end of file
+./culebratester2 install
+
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 8, 07:04 (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1034258
Default Alt Text
(135 KB)
Attached To
Mode
R134 rentgen-android
Attached
Detach File
Event Timeline
Log In to Comment