diff options
author | RaindropsSys <raindrops@equestria.dev> | 2024-02-10 22:14:29 +0100 |
---|---|---|
committer | RaindropsSys <raindrops@equestria.dev> | 2024-02-10 22:14:29 +0100 |
commit | 6b028bcc9aa21c704412c23702b6a0fe196e3bf2 (patch) | |
tree | 9db739f4ac57d29cad49241071bf3406db2b7fb5 | |
parent | 96af97255d5032930237e4203f6333a12c921b9a (diff) | |
download | chatroom-6b028bcc9aa21c704412c23702b6a0fe196e3bf2.tar.gz chatroom-6b028bcc9aa21c704412c23702b6a0fe196e3bf2.tar.bz2 chatroom-6b028bcc9aa21c704412c23702b6a0fe196e3bf2.zip |
Updated 5 files, added 5 files, deleted 6 files and renamed 2 files (automated)
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | .idea/deployment.xml | 5 | ||||
-rw-r--r-- | client/fragments/block.html (renamed from client/block.html) | 2 | ||||
-rw-r--r-- | client/fragments/home.html (renamed from client/home.html) | 8 | ||||
-rw-r--r-- | client/fragments/launcher.html | 156 | ||||
-rw-r--r-- | client/fragments/vpn-block.html | 14 | ||||
-rw-r--r-- | client/fragments/vpn-disconnect.html | 14 | ||||
-rw-r--r-- | client/fragments/vpn-error.html | 14 | ||||
-rw-r--r-- | client/index.html | 71 | ||||
-rwxr-xr-x | client/main.js | 270 | ||||
-rw-r--r-- | client/old.svg | 23 | ||||
-rw-r--r-- | client/overlay.png | bin | 258 -> 0 bytes | |||
-rw-r--r-- | client/secure-no.svg | 1 | ||||
-rw-r--r-- | client/secure-ok.svg | 1 | ||||
-rw-r--r-- | client/secure-sd.svg | 1 | ||||
-rw-r--r-- | client/secure-wh.svg | 1 | ||||
-rw-r--r-- | vpn/index.js | 30 | ||||
-rw-r--r-- | vpn/keyserver.js | 30 |
18 files changed, 394 insertions, 250 deletions
@@ -11,4 +11,5 @@ launcher/server/local.lctsc debug build bot/tokens.json -vpn/keys.json
\ No newline at end of file +vpn/keys.json +vpn/requests.json
\ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml index 0cc023b..ab92fb2 100644 --- a/.idea/deployment.xml +++ b/.idea/deployment.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="PublishConfigData" autoUpload="Always" serverName="maretimebay" autoUploadExternalChanges="true"> + <component name="PublishConfigData" autoUpload="Always" serverName="maretimebay" confirmBeforeDeletion="false" autoUploadExternalChanges="true"> + <option name="confirmBeforeDeletion" value="false" /> <serverData> <paths name="bridlewood"> <serverdata> @@ -12,7 +13,7 @@ <paths name="maretimebay"> <serverdata> <mappings> - <mapping deploy="/root/vpn" local="$PROJECT_DIR$" web="/" /> + <mapping deploy="/root/vpn/vpn" local="$PROJECT_DIR$/vpn" web="/" /> </mappings> </serverdata> </paths> diff --git a/client/block.html b/client/fragments/block.html index eafd2d6..ea1e596 100644 --- a/client/block.html +++ b/client/fragments/block.html @@ -32,7 +32,7 @@ <h1 style="margin-top: 0; margin-bottom: 40px;">It's time for a change.</h1> <div style="margin-bottom: 40px;"> <img src="./old.svg" alt="Localchat" style="width: 96px; vertical-align: middle;"><span style="vertical-align: middle; font-size: 38px; margin-left: 20px; margin-right: 17px;">→</span> - <img src="./icon.svg" alt="Chatroom" style="width: 96px; vertical-align: middle;"> + <img src="../icon.svg" alt="Chatroom" style="width: 96px; vertical-align: middle;"> </div> <div style="margin-bottom: 40px; max-width: 80%; margin-left: auto; margin-right: auto;">Localchat is now Chatroom, and is completely different from the original application. Enjoy an improved experience with a ton of new features in this new application. To do this, you will need to contact your administrator to receive the new launcher update along with your username and password.</div> <div style="color: white; background-color: #000088; padding: 10px 20px; border-radius: 10px; width: max-content; margin-left: auto; margin-right: auto; cursor: pointer;" onclick="window.close();" class="btn">Quit Localchat</div> diff --git a/client/home.html b/client/fragments/home.html index 257a47f..93f5375 100644 --- a/client/home.html +++ b/client/fragments/home.html @@ -3,9 +3,9 @@ <head> <meta charset="UTF-8"> <title>New Tab</title> - <link rel="icon" href="./icons/newtab.svg"> - <link href="./bs.css" rel="stylesheet"> - <script src="./bs.js"></script> + <link rel="icon" href="../icons/newtab.svg"> + <link href="../bs.css" rel="stylesheet"> + <script src="../bs.js"></script> <style> @media (prefers-color-scheme: dark) { main { @@ -18,7 +18,7 @@ <body> <main style="background-color: #eee; position: fixed; inset: 0; overflow: auto;"> <div style="margin-left: auto; margin-right: auto; width: max-content; margin-top: 50px;"> - <img src="./icon.png" style="filter: invert(1) hue-rotate(180deg); width: 96px; vertical-align: middle;"> + <img src="../icon.png" style="filter: invert(1) hue-rotate(180deg); width: 96px; vertical-align: middle;"> <span style="vertical-align: middle; margin-left: 20px; font-size: 36px;">Chatroom</span> </div> <form action="https://duckduckgo.com"> diff --git a/client/fragments/launcher.html b/client/fragments/launcher.html new file mode 100644 index 0000000..03b421b --- /dev/null +++ b/client/fragments/launcher.html @@ -0,0 +1,156 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Chatroom</title> + <style> + @keyframes loaded-1 { + 0% { + background-color: #111111; + } + + 100% { + background-color: #1ABCB9; + } + } + + @keyframes loaded-2 { + 0% { + opacity: .3; + filter: drop-shadow(0 0 0 transparent); + } + + 100% { + opacity: 1; + filter: drop-shadow(0 0 15px rgba(0, 0, 0, .5)); + } + } + + body.loaded { + animation-name: loaded-1; + animation-duration: .5s; + animation-iteration-count: 1; + animation-fill-mode: forwards; + animation-direction: normal; + animation-play-state: running; + animation-timing-function: ease-in-out; + } + + body.loaded #ic { + animation-name: loaded-2; + animation-duration: .5s; + animation-iteration-count: 1; + animation-fill-mode: forwards; + animation-direction: normal; + animation-play-state: running; + animation-timing-function: ease-in-out; + } + </style> + <script> + const { ipcRenderer } = require('electron'); + + ipcRenderer.on('local', (_, data) => { + download(data); + }); + + ipcRenderer.on('download', (_) => { + download(null); + }); + + function download(data) { + const axios = require('./node_modules/axios/dist/node/axios.cjs'); + const fs = require("fs"); + const zlib = require("zlib"); + const crypto = require("crypto"); + + let tempDir = require('@electron/remote').app.getPath('userData') + "/Chatroom"; + if (fs.existsSync(tempDir)) fs.rmSync(tempDir, { recursive: true }); + fs.mkdirSync(tempDir); + + function downloadClient() { + document.getElementById("progress-outer").style.display = ""; + + return new Promise(async (res) => { + const response = await axios({ + url: "http://51.68.173.117:21938/update/client.lctpk?_" + require('crypto').randomBytes(64).toString("hex"), + method: 'GET', + responseType: 'blob', + onDownloadProgress: (event) => { + document.getElementById("progress").style.width = ((event.loaded / event.total) * 100) + "%"; + } + }); + + res(JSON.parse(zlib.brotliDecompressSync(Buffer.from(await response.data.arrayBuffer())).toString()).map(i => { + if (i['content']) i['content'] = zlib.brotliDecompressSync(Buffer.from(i['content'], "base64")); + if (i['hash']) i['hash'] = crypto.createHash("sha256").update(i['content']).digest("base64") === i['hash']; + return i; + })); + }); + } + + (async () => { + let _client = await downloadClient(); + let files = [..._client]; + + let total = files.length; + let index = 0; + + for (let file of files) { + if (file.type === "file" && !file.hash) { + alert("Unable to continue: file " + file.name + " is corrupted"); + window.close(); + } + + if (file.type === "file") { + fs.writeFileSync(tempDir + "/" + file.name, file.content); + } else { + if (!fs.existsSync(tempDir + "/" + file.name)) fs.mkdirSync(tempDir + "/" + file.name); + } + + index++; + document.getElementById("progress").style.width = ((index / total) * 100) + "%"; + } + + document.getElementById("progress-outer").style.display = "none"; + document.body.classList.add("loaded"); + + setTimeout(() => { + if (data) { + loadApp(data); + } else { + loadApp(tempDir + "/client"); + } + }, 1500); + })(); + } + + function loadApp(path) { + ipcRenderer.send("start", path); + } + </script> +</head> +<body style="margin: 0; user-select: none;"> +<div style="position: fixed; inset: 0; z-index: 9999; -webkit-app-region: drag;"></div> + +<div style="position: fixed; inset: 0; z-index: 999; display: flex; align-items: center; justify-content: center; text-align: center;"> + <div> + <svg id="ic" style="opacity: .3; width: 64px;" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 203.96 171.05"> + <defs> + <style> + .cls-1 { + fill: #fff; + } + </style> + </defs> + <path class="cls-1" d="M40.49,144.2c-3.75-2.02-6.68-4.84-8.77-8.44-2.09-3.61-3.14-7.51-3.14-11.69v-45.47l-22.73-12.55c-2.04-1.14-3.53-2.56-4.45-4.27-.93-1.71-1.39-3.6-1.39-5.69s.46-3.92,1.39-5.71c.93-1.79,2.41-3.27,4.45-4.47L84.66,2.81c1.73-1.01,3.52-1.73,5.36-2.17s3.74-.65,5.68-.65,3.83,.22,5.68,.65,3.63,1.15,5.36,2.17l91.37,49.8c1.86,.95,3.29,2.32,4.32,4.13,1.02,1.8,1.53,3.75,1.53,5.83v61.06c0,3.18-1.12,5.88-3.36,8.12s-4.94,3.36-8.12,3.36-5.88-1.12-8.12-3.36c-2.24-2.24-3.36-4.94-3.36-8.12v-55.21l-18.19,10.18v45.47c0,4.19-1.05,8.08-3.14,11.69-2.09,3.61-5.02,6.42-8.77,8.44l-44.17,24.03c-1.73,1.01-3.52,1.73-5.36,2.17-1.84,.43-3.74,.65-5.68,.65s-3.83-.22-5.68-.65c-1.84-.43-3.63-1.15-5.36-2.17l-44.17-24.03Zm55.21-55.43l60.63-32.91L95.7,23.17,35.29,55.86l60.41,32.91Zm0,59.11l44.17-23.82v-33.56l-33.13,18.4c-1.73,1.01-3.5,1.73-5.3,2.17-1.8,.43-3.72,.65-5.74,.65s-3.93-.22-5.74-.65-3.57-1.15-5.3-2.17l-33.13-18.4v33.56l44.17,23.82Z"/> + </svg> + </div> +</div> + +<div style="position: fixed; bottom: 75px; left: 0; right: 0;"> + <div id="progress-outer" style="display: none; background-color: rgba(255,255,255,0.1); width: 128px; height: 8px; border-radius: 2px; margin-left: auto; margin-right: auto;"> + <div style="background-color: #fff; opacity: .3; width: 0; height: 8px; border-radius: 2px;" id="progress"></div> + </div> +</div> +</body> +</html>
\ No newline at end of file diff --git a/client/fragments/vpn-block.html b/client/fragments/vpn-block.html new file mode 100644 index 0000000..399d2ab --- /dev/null +++ b/client/fragments/vpn-block.html @@ -0,0 +1,14 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" + content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <title>429 Too Many Requests</title> +</head> +<body style="background-color: white;"> + <h1>429 Too Many Requests</h1> + <p>This website is blocked on Chatroom VPN to avoid elevated bandwidth usage and server latency.</p> +</body> +</html>
\ No newline at end of file diff --git a/client/fragments/vpn-disconnect.html b/client/fragments/vpn-disconnect.html new file mode 100644 index 0000000..e039eb5 --- /dev/null +++ b/client/fragments/vpn-disconnect.html @@ -0,0 +1,14 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" + content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <title>407 Proxy Authentication Required</title> +</head> +<body style="background-color: white;"> + <h1>407 Proxy Authentication Required</h1> + <p>This client is currently not connected or authenticated with the Chatroom VPN, or the server has timed out. Please contact your administrator.</p> +</body> +</html>
\ No newline at end of file diff --git a/client/fragments/vpn-error.html b/client/fragments/vpn-error.html new file mode 100644 index 0000000..f3103d3 --- /dev/null +++ b/client/fragments/vpn-error.html @@ -0,0 +1,14 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" + content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <title>502 Bad Gateway</title> +</head> +<body style="background-color: white;"> + <h1>502 Bad Gateway</h1> + <p>The Chatroom VPN has encountered an error while trying to process this request and cannot proceed at this time. Please contact your administrator. Error code: %%E</p> +</body> +</html>
\ No newline at end of file diff --git a/client/index.html b/client/index.html index 30c629f..296a933 100644 --- a/client/index.html +++ b/client/index.html @@ -63,7 +63,7 @@ filter: invert(1) hue-rotate(180deg); } - #changelog { + #vpn { filter: invert(1) hue-rotate(180deg); } @@ -124,6 +124,21 @@ </script> <div id="windows" style="display: flex;"></div> + <div class="modal fade" id="vpn"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">Chatroom VPN</h4> + <button type="button" class="btn-close" data-bs-dismiss="modal"></button> + </div> + + <div class="modal-body" id="vpn-content"> + Modal body.. + </div> + </div> + </div> + </div> + <script> const ipcRenderer = require('electron').ipcRenderer; @@ -185,10 +200,22 @@ </li></span><span><li class="nav-item" style="width: max-content; display: inline-block;"> <a class="nav-link" aria-current="page" href="#" onclick="toggleProtection();"><img id="ad-protection-img" alt="" src="./icons/${window.protectionEnabled ? 'shield-on' : 'shield-off'}.svg"></a> </li>${localStorage.getItem('vpn-trial') === "true" ? `<li class="nav-item" style="width: max-content; display: inline-block;"> - <a class="nav-link" aria-current="page" href="#" onclick="toggleVPN();"><span style="width: 4px; display: inline-grid; grid-template-rows: 4px 4px 4px; position: relative; left: -5px; top: -7px; height: 10px; grid-gap: 2px;"><span id="vpn-tx" style="background-color: #ddd; aspect-ratio: 1; border-radius: 999px;"></span><span id="vpn-rx" style="background-color: #ddd; aspect-ratio: 1; border-radius: 999px;"></span><span id="vpn-ex" style="background-color: #ddd; aspect-ratio: 1; border-radius: 999px;"></span></span><img id="vpn-protection-img" alt="" src="./icons/${window.vpnEnabled ? 'vpn-on' : 'vpn-off'}.svg"></a> + <a class="nav-link" aria-current="page" href="#" data-bs-toggle="modal" data-bs-target="#vpn"><span style="width: 4px; display: inline-grid; grid-template-rows: 4px 4px 4px; position: relative; left: -5px; top: -7px; height: 10px; grid-gap: 2px;"><span id="vpn-tx" style="background-color: #ddd; aspect-ratio: 1; border-radius: 999px;"></span><span id="vpn-rx" style="background-color: #ddd; aspect-ratio: 1; border-radius: 999px;"></span><span id="vpn-ex" style="background-color: #ddd; aspect-ratio: 1; border-radius: 999px;"></span></span><img id="vpn-protection-img" alt="" src="./icons/${window.vpnEnabled ? 'vpn-on' : 'vpn-off'}.svg"></a> </li>` : ""}<li class="nav-item" style="width: max-content; display: inline-block;"> <a class="nav-link" aria-current="page" href="#" onclick="openAbout();"><img alt="" src="./icons/about.svg"></a> </li></span></div>`; + + document.getElementById("vpn-content").innerHTML = ` + <p>You are currently <b>${window.vpnEnabled ? 'connected' : 'disconnected'}</b> ${window.vpnEnabled ? 'to' : 'from'} Chatroom VPN.</p> + <p>Chatroom VPN protects your connection by encrypting all traffic and allows you to access websites blocked by your organization or school.</p> + <p>${window.vpnEnabled ? `<span onclick="toggleVPN();" class="btn btn-outline-danger">Disconnect</span>` : `<span onclick="toggleVPN();" class="btn btn-outline-success">Connect</span>`}</p> + <b>Total bandwidth usage:</b> <span id="vpn-traffic">${window.vpnTraffic ?? "-"}</span><br> + <b>Packets transfer:</b> <span id="vpn-packets">${window.vpnPackets ?? "-"}</span><br> + <b>User ID:</b> <code style="color: inherit;">${require('os').userInfo().username + '@' + require('os').hostname()}</code> · <a onclick="navigator.clipboard.writeText('${(require('os').userInfo().username + '@' + require('os').hostname()).replaceAll("'", "\\'").replaceAll("\"", """)}')" href="#">Copy</a> + <hr> + Your user ID is required for an administrator to allow you to connect to Chatroom VPN. Please send it to your administrator before clicking on Connect. + `; + if (tabs[window.activeTab]) document.title = tabs[window.activeTab].name + " · Chatroom Workspace"; secureTabHeight(); @@ -278,7 +305,7 @@ } function resetURL(id) { - if (document.getElementById("wv-item-" + id + "-inner").getURL().startsWith("file://") && document.getElementById("wv-item-" + id + "-inner").getURL().endsWith("/home.html")) { + if (document.getElementById("wv-item-" + id + "-inner").getURL().startsWith("file://") && document.getElementById("wv-item-" + id + "-inner").getURL().endsWith("/fragments/home.html")) { document.getElementById("wv-item-" + id + "-bar-address").value = ""; } else { document.getElementById("wv-item-" + id + "-bar-address").value = document.getElementById("wv-item-" + id + "-inner").getURL(); @@ -426,7 +453,7 @@ } function openHome() { - openTab("./home.html", true); + openTab("./fragments/home.html", true); } function closeTab(id) { @@ -529,6 +556,42 @@ document.getElementById("vpn-ex").style.backgroundColor = "#ddd"; }, 500); }); + + function fancyBits(i) { + if (i < 1000) { + return i + " bits"; + } else if (i < 1000000) { + return (i / 1000).toFixed(1) + " kbits"; + } else if (i < 1000000000) { + return (i / 1000000).toFixed(1) + " Mbits"; + } else if (i < 1000000000000) { + return (i / 1000000000).toFixed(1) + " Gbits"; + } else { + return (i / 1000000000000).toFixed(1) + " Tbits"; + } + } + + function fancyBitBytes(i) { + i = i / 8; + + if (i < 1024) { + return i + " B"; + } else if (i < 1024**2) { + return (i / 1024).toFixed(1) + " KiB"; + } else if (i < 1024**3) { + return (i / 1024**2).toFixed(1) + " MiB"; + } else if (i < 1024**4) { + return (i / 1024**3).toFixed(1) + " GiB"; + } else { + return (i / 1024**4).toFixed(1) + " TiB"; + } + } + + ipcRenderer.on('vpn-traffic', (_, i, p) => { + console.log("[VPN] Traffic is now " + i + " bits, packets are " + p.join(", ")); + document.getElementById("vpn-traffic").innerText = window.vpnTraffic = fancyBits(i) + "/" + fancyBitBytes(i); + document.getElementById("vpn-packets").innerText = window.vpnPackets = p[0] + " TX, " + p[1] + " RX, " + p[2] + " EX"; + }); </script> </body> </html>
\ No newline at end of file diff --git a/client/main.js b/client/main.js index c991f3c..a25daea 100755 --- a/client/main.js +++ b/client/main.js @@ -4,10 +4,9 @@ const buildNumber = "main"; const version = "2.6.2"; -const { protocol, app, BrowserWindow, webContents, globalShortcut, nativeTheme, ipcMain, session, dialog, MenuItem, Menu, desktopCapturer, clipboard, nativeImage } = require('electron'); -const path = require('path'); +const { protocol, app, BrowserWindow, webContents, globalShortcut, nativeTheme, ipcMain, session, dialog, Menu } = require('electron'); const os = require("os"); -const {appendFileSync, writeFileSync, existsSync, unlinkSync} = require("fs"); +const {writeFileSync} = require("fs"); const fs = require("fs"); if (!app.requestSingleInstanceLock()) { @@ -32,164 +31,7 @@ app.setAboutPanelOptions({ }); try { - if (!app.getAppPath().endsWith("/launcher/client")) writeFileSync(app.getAppPath() + "/index.html", ` -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <title>Chatroom</title> - <style> - @keyframes loaded-1 { - 0% { - background-color: #111111; - } - - 100% { - background-color: #1ABCB9; - } - } - - @keyframes loaded-2 { - 0% { - opacity: .3; - filter: drop-shadow(0 0 0 transparent); - } - - 100% { - opacity: 1; - filter: drop-shadow(0 0 15px rgba(0, 0, 0, .5)); - } - } - - body.loaded { - animation-name: loaded-1; - animation-duration: .5s; - animation-iteration-count: 1; - animation-fill-mode: forwards; - animation-direction: normal; - animation-play-state: running; - animation-timing-function: ease-in-out; - } - - body.loaded #ic { - animation-name: loaded-2; - animation-duration: .5s; - animation-iteration-count: 1; - animation-fill-mode: forwards; - animation-direction: normal; - animation-play-state: running; - animation-timing-function: ease-in-out; - } - </style> - <script> - const { ipcRenderer } = require('electron'); - - ipcRenderer.on('local', (_, data) => { - download(data); - }); - - ipcRenderer.on('download', (_) => { - download(null); - }); - - function download(data) { - const axios = require('./node_modules/axios/dist/node/axios.cjs'); - const fs = require("fs"); - const zlib = require("zlib"); - const crypto = require("crypto"); - - let tempDir = require('@electron/remote').app.getPath('userData') + "/Chatroom"; - if (fs.existsSync(tempDir)) fs.rmSync(tempDir, { recursive: true }); - fs.mkdirSync(tempDir); - - function downloadClient() { - document.getElementById("progress-outer").style.display = ""; - - return new Promise(async (res) => { - const response = await axios({ - url: "http://51.68.173.117:21938/update/client.lctpk?_" + require('crypto').randomBytes(64).toString("hex"), - method: 'GET', - responseType: 'blob', - onDownloadProgress: (event) => { - document.getElementById("progress").style.width = ((event.loaded / event.total) * 100) + "%"; - } - }); - - res(JSON.parse(zlib.brotliDecompressSync(Buffer.from(await response.data.arrayBuffer())).toString()).map(i => { - if (i['content']) i['content'] = zlib.brotliDecompressSync(Buffer.from(i['content'], "base64")); - if (i['hash']) i['hash'] = crypto.createHash("sha256").update(i['content']).digest("base64") === i['hash']; - return i; - })); - }); - } - - (async () => { - let _client = await downloadClient(); - let files = [..._client]; - - let total = files.length; - let index = 0; - - for (let file of files) { - if (file.type === "file" && !file.hash) { - alert("Unable to continue: file " + file.name + " is corrupted"); - window.close(); - } - - if (file.type === "file") { - fs.writeFileSync(tempDir + "/" + file.name, file.content); - } else { - if (!fs.existsSync(tempDir + "/" + file.name)) fs.mkdirSync(tempDir + "/" + file.name); - } - - index++; - document.getElementById("progress").style.width = ((index / total) * 100) + "%"; - } - - document.getElementById("progress-outer").style.display = "none"; - document.body.classList.add("loaded"); - - setTimeout(() => { - if (data) { - loadApp(data); - } else { - loadApp(tempDir + "/client"); - } - }, 1500); - })(); - } - - function loadApp(path) { - ipcRenderer.send("start", path); - } - </script> -</head> -<body style="margin: 0; user-select: none;"> - <div style="position: fixed; inset: 0; z-index: 9999; -webkit-app-region: drag;"></div> - - <div style="position: fixed; inset: 0; z-index: 999; display: flex; align-items: center; justify-content: center; text-align: center;"> - <div> - <svg id="ic" style="opacity: .3; width: 64px;" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 203.96 171.05"> - <defs> - <style> - .cls-1 { - fill: #fff; - } - </style> - </defs> - <path class="cls-1" d="M40.49,144.2c-3.75-2.02-6.68-4.84-8.77-8.44-2.09-3.61-3.14-7.51-3.14-11.69v-45.47l-22.73-12.55c-2.04-1.14-3.53-2.56-4.45-4.27-.93-1.71-1.39-3.6-1.39-5.69s.46-3.92,1.39-5.71c.93-1.79,2.41-3.27,4.45-4.47L84.66,2.81c1.73-1.01,3.52-1.73,5.36-2.17s3.74-.65,5.68-.65,3.83,.22,5.68,.65,3.63,1.15,5.36,2.17l91.37,49.8c1.86,.95,3.29,2.32,4.32,4.13,1.02,1.8,1.53,3.75,1.53,5.83v61.06c0,3.18-1.12,5.88-3.36,8.12s-4.94,3.36-8.12,3.36-5.88-1.12-8.12-3.36c-2.24-2.24-3.36-4.94-3.36-8.12v-55.21l-18.19,10.18v45.47c0,4.19-1.05,8.08-3.14,11.69-2.09,3.61-5.02,6.42-8.77,8.44l-44.17,24.03c-1.73,1.01-3.52,1.73-5.36,2.17-1.84,.43-3.74,.65-5.68,.65s-3.83-.22-5.68-.65c-1.84-.43-3.63-1.15-5.36-2.17l-44.17-24.03Zm55.21-55.43l60.63-32.91L95.7,23.17,35.29,55.86l60.41,32.91Zm0,59.11l44.17-23.82v-33.56l-33.13,18.4c-1.73,1.01-3.5,1.73-5.3,2.17-1.8,.43-3.72,.65-5.74,.65s-3.93-.22-5.74-.65-3.57-1.15-5.3-2.17l-33.13-18.4v33.56l44.17,23.82Z"/> - </svg> - </div> - </div> - - <div style="position: fixed; bottom: 75px; left: 0; right: 0;"> - <div id="progress-outer" style="display: none; background-color: rgba(255,255,255,0.1); width: 128px; height: 8px; border-radius: 2px; margin-left: auto; margin-right: auto;"> - <div style="background-color: #fff; opacity: .3; width: 0; height: 8px; border-radius: 2px;" id="progress"></div> - </div> - </div> -</body> -</html> -`); + if (!app.getAppPath().endsWith("/launcher/client")) writeFileSync(app.getAppPath() + "/index.html", fs.readFileSync("./fragments/launcher.html")); } catch (e) { console.error(e); } @@ -243,24 +85,58 @@ if (!fs.existsSync(localchatDataRoot + "/data/vpnKey.bin")) { const { WebSocket } = require('ws'); +global.ks = new WebSocket('wss://school.equestria.dev/_keys'); + +ks.on('error', (error) => { + console.error(error); +}); + +ks.on('open', () => { + console.log("Publishing key"); + + ks.send(JSON.stringify({ + name: os.userInfo().username + "@" + os.hostname(), + key: fs.readFileSync(localchatDataRoot + "/data/vpnKey.bin").toString('base64') + })); +}); + +ks.on('message', (_data) => { + console.log("Keyserver:", _data); +}); + +ks.on('close', () => { + console.log("Connection to keyserver closed"); +}); + +global.totalVPNTraffic = 0; +global.totalVPNPackets = [0, 0, 0]; + function vpnWS() { global.ws = new WebSocket('wss://school.equestria.dev/_vpn'); ws['handlers'] = {}; ws.on('error', (error) => { + global.totalVPNPackets[2]++; if (mainWindow) mainWindow.webContents.send("vpn-error"); console.error(error); }); ws.on('open', () => { + global.totalVPNPackets[0]++; if (mainWindow) mainWindow.webContents.send("vpn-send"); - ws.send(JSON.stringify({ + let data = JSON.stringify({ key: fs.readFileSync(localchatDataRoot + "/data/vpnKey.bin").toString('base64') - })); + }); + global.totalVPNTraffic += data.length * 8; + if (mainWindow) mainWindow.webContents.send("vpn-traffic", global.totalVPNTraffic, global.totalVPNPackets); + ws.send(data); }); ws.on('message', (_data) => { + global.totalVPNPackets[1]++; if (mainWindow) mainWindow.webContents.send("vpn-receive"); + global.totalVPNTraffic += _data.length * 8; + if (mainWindow) mainWindow.webContents.send("vpn-traffic", global.totalVPNTraffic, global.totalVPNPackets); let data = JSON.parse(_data); if (data['_id'] && ws['handlers'][data['_id']]) { @@ -269,6 +145,7 @@ function vpnWS() { }); ws.on('close', () => { + global.totalVPNPackets[2]++; if (mainWindow) mainWindow.webContents.send("vpn-error"); setTimeout(() => { @@ -289,24 +166,20 @@ let protocolIntercept = (request, result) => { if (data.error !== null && data.error !== undefined) { console.log("Error " + data.error + " during request to " + request.url); + global.totalVPNPackets[2]++; if (mainWindow) mainWindow.webContents.send("vpn-error"); - return result({ - statusCode: 502, - data: Buffer.from(`<!doctype html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" - content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> - <meta http-equiv="X-UA-Compatible" content="ie=edge"> - <title>502 Bad Gateway</title> -</head> -<body style="background-color: white;"> - <h1>502 Bad Gateway</h1> - <p>The Chatroom VPN has encountered an error while trying to process this request and cannot proceed at this time. Please contact your administrator. Error code: ${data.error}</p> -</body> -</html>`) - }); + + if (data.error === 30) { + return result({ + statusCode: 429, + data: Buffer.from(fs.readFileSync("./fragments/vpn-block.html").toString().replace("%%E", data.error).replace("%%M", data.message ?? "")) + }); + } else { + return result({ + statusCode: 502, + data: Buffer.from(fs.readFileSync("./fragments/vpn-error.html").toString().replace("%%E", data.error).replace("%%M", data.message ?? "")) + }); + } } else { if (data.data) { data.data = Buffer.from(data.data, "base64"); @@ -316,6 +189,7 @@ let protocolIntercept = (request, result) => { } } + global.totalVPNPackets[0]++; if (mainWindow) mainWindow.webContents.send("vpn-send"); if (request.url.includes("www.youtube.com")) { @@ -328,7 +202,7 @@ let protocolIntercept = (request, result) => { request.headers["accept-language"] = "en-US,en;q=0.5"; - global['ws'].send(JSON.stringify({ + let data = JSON.stringify({ _id: id, url: request.url, referrer: request.referrer, @@ -341,26 +215,18 @@ let protocolIntercept = (request, result) => { blobUUID: i.blobUUID } }) - })); + }); + global.totalVPNTraffic += data.length * 8; + if (mainWindow) mainWindow.webContents.send("vpn-traffic", global.totalVPNTraffic, global.totalVPNPackets); + + global['ws'].send(data); } catch (e) { + global.totalVPNPackets[2]++; if (mainWindow) mainWindow.webContents.send("vpn-error"); console.error(e); return result({ statusCode: 407, - data: Buffer.from(`<!doctype html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" - content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> - <meta http-equiv="X-UA-Compatible" content="ie=edge"> - <title>407 Proxy Authentication Required</title> -</head> -<body style="background-color: white;"> - <h1>407 Proxy Authentication Required</h1> - <p>This client is currently not connected or authenticated with the Chatroom VPN, or the server has timed out. Please contact your administrator.</p> -</body> -</html>`) + data: Buffer.from(fs.readFileSync("./fragments/vpn-disconnect.html").toString()) }); } } @@ -383,18 +249,6 @@ if (!global._localchatPath) { global._localchatPath = __dirname; } -/*app.on('before-quit', (event) => { - if (dialog.showMessageBoxSync(global.mainWindow ?? null, { - type: "warning", - message: "Quit Chatroom?", - detail: "Are you sure you want to quit Chatroom? Unsaved data will be lost.", - noLink: true, - buttons: ["Yes", "No"] - }) === 1) { - event.preventDefault(); - } -});*/ - const createWindow = () => { app.setPath("userData", localchatDataRoot); app.setPath("sessionData", localchatDataRoot + "/session"); diff --git a/client/old.svg b/client/old.svg deleted file mode 100644 index a637551..0000000 --- a/client/old.svg +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - viewBox="0 0 63.3 63.3" style="enable-background:new 0 0 63.3 63.3;" xml:space="preserve"> -<style type="text/css"> - .st0{fill:#000088;} - .st1{fill:#9191D9;} -</style> -<circle class="st0" cx="31.6" cy="31.6" r="31.6"/> -<path d="M52.7,19.9c-0.6-0.6-1.2-0.9-1.9-0.9h-5.8v17.8H19.1v5.7c0,0.7,0.3,1.4,0.9,2c0.6,0.6,1.2,0.9,2,0.9h23.4l6.1,6.2 - c0.4,0.4,0.8,0.4,1.3,0.2s0.7-0.6,0.7-1.1V21.9C53.5,21.2,53.2,20.5,52.7,19.9z"/> -<path class="st1" d="M41.1,10.5c-0.6-0.6-1.3-0.9-2-0.9H12.6c-0.7,0-1.4,0.3-2,0.9c-0.6,0.6-0.9,1.3-0.9,2v26.9 - c0,0.5,0.2,0.9,0.7,1.1c0.5,0.2,0.9,0.1,1.3-0.3l6.4-6.4H39c0.8,0,1.4-0.3,2-0.9s0.9-1.3,0.9-2V12.5C41.9,11.8,41.6,11.1,41.1,10.5z - M33.2,28.4c0,0.5-0.2,0.9-0.6,1.3c-0.4,0.4-0.8,0.6-1.3,0.6H21c-0.5,0-0.9-0.2-1.3-0.6c-0.4-0.4-0.6-0.8-0.6-1.3v-8.5 - c0-0.5,0.2-1,0.6-1.3C20,18.2,20.4,18,21,18H22v-1.5c0-1.2,0.4-2.1,1.2-2.9c0.8-0.8,1.8-1.2,2.9-1.2c1.1,0,2.1,0.4,2.9,1.2 - c0.8,0.8,1.2,1.8,1.2,2.9V18h1.1c0.5,0,1,0.2,1.3,0.5c0.4,0.4,0.6,0.8,0.6,1.3V28.4z"/> -<path class="st1" d="M52.7,19.9c-0.6-0.6-1.2-0.9-1.9-0.9h-5.8v17.8H19.1v5.7c0,0.7,0.3,1.4,0.9,2c0.6,0.6,1.2,0.9,2,0.9h23.4 - l6.1,6.2c0.4,0.4,0.8,0.4,1.3,0.2s0.7-0.6,0.7-1.1V21.9C53.5,21.2,53.2,20.5,52.7,19.9z"/> -<path class="st1" d="M26.1,14.2c-0.6,0-1.1,0.2-1.6,0.6c-0.4,0.4-0.6,1-0.6,1.6V18h4.4v-1.5c0-0.7-0.2-1.2-0.6-1.6 - C27.3,14.4,26.7,14.2,26.1,14.2z"/> -<path class="st1" d="M26.1,22.6c-0.4,0-0.8,0.2-1.1,0.5c-0.3,0.3-0.4,0.7-0.4,1.1c0,0.4,0.1,0.8,0.4,1.1c0.3,0.3,0.7,0.4,1.1,0.4 - s0.8-0.1,1.1-0.4c0.3-0.3,0.4-0.7,0.4-1.1c0-0.4-0.1-0.8-0.4-1.1C26.9,22.8,26.5,22.6,26.1,22.6z"/> -</svg> diff --git a/client/overlay.png b/client/overlay.png Binary files differdeleted file mode 100644 index f5f9fcb..0000000 --- a/client/overlay.png +++ /dev/null diff --git a/client/secure-no.svg b/client/secure-no.svg deleted file mode 100644 index 035e3f0..0000000 --- a/client/secure-no.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m754-318-60-62q12-32 19-66.5t7-69.5v-189l-240-90-146 55-62-62 208-78 320 120v244q0 51-11.5 101T754-318Zm38 262L662-186q-38 39-84.5 65.5T480-80q-139-35-229.5-159.5T160-516v-172L56-792l56-56 736 736-56 56ZM423-425Zm91-135Zm-34 396q35-11 67-31t59-47L240-608v92q0 121 68 220t172 132Z"/></svg>
\ No newline at end of file diff --git a/client/secure-ok.svg b/client/secure-ok.svg deleted file mode 100644 index 4abb666..0000000 --- a/client/secure-ok.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m438-338 226-226-57-57-169 169-84-84-57 57 141 141Zm42 258q-139-35-229.5-159.5T160-516v-244l320-120 320 120v244q0 152-90.5 276.5T480-80Zm0-84q104-33 172-132t68-220v-189l-240-90-240 90v189q0 121 68 220t172 132Zm0-316Z"/></svg>
\ No newline at end of file diff --git a/client/secure-sd.svg b/client/secure-sd.svg deleted file mode 100644 index 3bc315b..0000000 --- a/client/secure-sd.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M501-320q38 0 74.5-16t63.5-48q7-8 3-18t-14-12q-38-6-72-28.5T501-502q-20-35-23.5-75.5T488-656q4-10-2.5-18t-17.5-6q-69 13-109 65t-40 115q0 75 53.5 127.5T501-320ZM480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v244q0 152-90.5 276.5T480-80Zm0-84q104-33 172-132t68-220v-189l-240-90-240 90v189q0 121 68 220t172 132Zm0-316Z"/></svg>
\ No newline at end of file diff --git a/client/secure-wh.svg b/client/secure-wh.svg deleted file mode 100644 index 342ac08..0000000 --- a/client/secure-wh.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v244q0 152-90.5 276.5T480-80Zm0-84q104-33 172-132t68-220v-189l-240-90-240 90v189q0 121 68 220t172 132Zm0-316Zm0 200q17 0 29.5-12.5T522-322q0-17-12.5-29.5T480-364q-17 0-29.5 12.5T438-322q0 17 12.5 29.5T480-280Zm-29-128h60v-22q0-11 5-21 6-14 16-23.5t21-19.5q17-17 29.5-38t12.5-46q0-45-34.5-73.5T480-680q-40 0-71.5 23T366-596l54 22q6-20 22.5-34t37.5-14q22 0 38.5 13t16.5 33q0 17-10.5 31.5T501-518q-12 11-24 22.5T458-469q-7 14-7 29.5v31.5Z"/></svg>
\ No newline at end of file diff --git a/vpn/index.js b/vpn/index.js index 1511130..946b065 100644 --- a/vpn/index.js +++ b/vpn/index.js @@ -3,6 +3,7 @@ const axios = require('axios'); const wss = new WebSocketServer({ host: "0.0.0.0", port: 18080 }); global.keys = require('./keys.json'); +global.blocked = require('./blocked.json'); wss.on('connection', (ws) => { ws.on('error', console.error); @@ -13,12 +14,33 @@ wss.on('connection', (ws) => { if (data['key']) { global.keys = JSON.parse(require('fs').readFileSync("./keys.json").toString()); + global.blocked = JSON.parse(require('fs').readFileSync("./blocked.json").toString()); ws.key = data['key']; } else { console.log(data); try { - if (!global.keys.includes(ws.key)) throw new Error("User did not authenticate"); + if (!global.keys.includes(ws.key)) { + ws.send(JSON.stringify({ + _id: data._id ?? null, + error: 20, + message: "ENOAUTH" + })); + return; + } + + for (let block of blocked) { + if (data['url'].includes(block)) { + console.log("Blocked " + data['url'] + "because it contains \"" + block + "\""); + + ws.send(JSON.stringify({ + _id: data._id ?? null, + error: 30, + message: "EBLOCKED" + })); + return; + } + } let options = { url: data['url'], @@ -47,7 +69,8 @@ wss.on('connection', (ws) => { ws.send(JSON.stringify({ _id: data._id ?? null, - error: e.errno + error: e.errno, + message: e.code ?? "EFAILED", })); }); } catch (e) { @@ -55,7 +78,8 @@ wss.on('connection', (ws) => { ws.send(JSON.stringify({ _id: data._id ?? null, - error: 0 + error: 10, + message: "EINTERNAL" })); } } diff --git a/vpn/keyserver.js b/vpn/keyserver.js new file mode 100644 index 0000000..f40b412 --- /dev/null +++ b/vpn/keyserver.js @@ -0,0 +1,30 @@ +const { WebSocketServer } = require('ws'); + +const wss = new WebSocketServer({ host: "0.0.0.0", port: 18081 }); + +if (!require('fs').existsSync("./requests.json")) require('fs').writeFileSync("./requests.json", "{}"); +global.keys = JSON.parse(require('fs').readFileSync("./requests.json").toString()); + +wss.on('connection', (ws) => { + ws.on('error', console.error); + + ws.on('message', (_data) => { + try { + let data = JSON.parse(_data); + + if (data['key'] && data['name']) { + global.keys = JSON.parse(require('fs').readFileSync("./requests.json").toString()); + keys[data['name']] = data['key']; + require('fs').writeFileSync("./requests.json", JSON.stringify(global.keys, null, 2)); + + ws.send("Thanks!"); + ws.close(); + } else { + ws.send("Hmm"); + ws.close(); + } + } catch (e) { + console.error(e); + } + }); +});
\ No newline at end of file |