diff --git a/lib/functions/welcome.dart b/lib/functions/welcome.dart index 68b585f..67efad8 100644 --- a/lib/functions/welcome.dart +++ b/lib/functions/welcome.dart @@ -1,7 +1,32 @@ import 'package:flutter/material.dart'; import 'dart:io'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:open_url/open_url.dart'; +import 'dart:math'; -Widget welcome(next, connected) { +String getKey(int length, String key, setState) { + if (key == "" || key.length < length) { + const ch = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + Random r = Random(); + String key = String.fromCharCodes( + Iterable.generate(length, (_) => ch.codeUnitAt(r.nextInt(ch.length)))); + setState(key); + print(key); + return key; + } + print(key); + return key; +} + +Widget welcome( + next, + connected, + localIp, + key, + setState, +) { + List ipSplit = localIp.split('.'); + String name = "crystal" + ipSplit.last; return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -130,7 +155,30 @@ Widget welcome(next, connected) { style: TextStyle(fontSize: 20, color: Colors.white)), const Text( "it's recommended to try that first to see if everything works", - style: TextStyle(fontSize: 20, color: Colors.white)) + style: TextStyle(fontSize: 20, color: Colors.white)), + const SizedBox(height: 20), + Tooltip( + message: "The qr code to scan for jade_batch, click to find out more.", + child: ElevatedButton( + onPressed: () { + openUrl("https://wiki.getcryst.al/index.php/Jade_batch"); + }, + style: TextButton.styleFrom( + primary: const Color.fromARGB(0, 23, 23, 23), + backgroundColor: const Color.fromARGB(0, 23, 23, 23), + elevation: 0, + padding: EdgeInsets.zero, + ), + child: QrImage( + data: "{\"ip\":\"$localIp\",\"name\":\"$name\",\"key\":\"" + + getKey(32, key, setState) + + "\"}", + embeddedImage: const AssetImage("assets/jade_logo.png"), + foregroundColor: Colors.white, + size: 150.150, + ), + ), + ), ], ); } diff --git a/lib/main.dart b/lib/main.dart index 11a0bdf..289ad51 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -85,6 +85,16 @@ void setWindowSize() { setWindowMaxSize(const Size(1300, 870)); } +localIP(setState) async { + for (var interface in await NetworkInterface.list()) { + for (var addr in interface.addresses) { + if (!addr.address.contains(":")) { + setState(addr.address); + } + } + } +} + void main() => runApp( const MaterialApp( debugShowCheckedModeBanner: false, @@ -102,6 +112,16 @@ Future setPassword(clearPass, setState) async { setState(password.replaceAll("\n", "")); } +Future startWebsocket(key, running, setRunning) async { + if (!running && key != "") { + Process.run("/opt/jade_gui/scripts/websocket.py", [key], runInShell: true) + .then((ProcessResult result) { + return result.stdout; + }); + setRunning(true); + } +} + class Jadegui extends StatefulWidget { const Jadegui({Key? key}) : super(key: key); @@ -131,6 +151,7 @@ class _JadeguiState extends State { bool runningPartMan = false; bool runningSum = false; bool runningEfi = false; + bool runningWebsocket = false; String clearPass = ""; String password = ""; String confirmPassword = ""; @@ -147,12 +168,24 @@ class _JadeguiState extends State { String crystalRoot = ""; String unakiteEfiDir = ""; String unakiteBootDev = ""; + String ip = ""; + String key = ""; Desktop currDesktop = desktops[0]; Keymap chosenLayout = Keymap(); List partitions = []; @override Widget build(BuildContext context) { + startWebsocket(key, runningWebsocket, (value) { + setState(() { + runningWebsocket = value; + }); + }); + localIP((value) { + setState(() { + ip = value; + }); + }); getPartition( (value) { setState(() { @@ -377,6 +410,13 @@ class _JadeguiState extends State { }); }, connected, + ip, + key, + (value) { + setState(() { + key = value; + }); + }, ), ), ), diff --git a/pubspec.lock b/pubspec.lock index d60aedd..5911076 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -35,7 +35,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" charcode: dependency: transitive description: @@ -137,6 +137,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + open_url: + dependency: "direct main" + description: + name: open_url + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" path: dependency: transitive description: @@ -249,6 +256,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + qr: + dependency: transitive + description: + name: qr + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" sky_engine: dependency: transitive description: flutter @@ -260,7 +281,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -281,7 +302,7 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" synchronized: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 49a706b..887ed91 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: path: plugins/window_size ref: a738913c8ce2c9f47515382d40827e794a334274 adwaita_icons: ^0.2.1 + qr_flutter: ^4.0.0 + open_url: ^2.0.0 dev_dependencies: diff --git a/scripts/getDiskType.sh b/scripts/getDiskType.sh old mode 100644 new mode 100755 diff --git a/scripts/websocket.py b/scripts/websocket.py new file mode 100755 index 0000000..3c9ad7a --- /dev/null +++ b/scripts/websocket.py @@ -0,0 +1,70 @@ +#!/usr/bin/python +import asyncio +import websockets +import json +import subprocess +import os +import sys +from Crypto.Cipher import AES +from Crypto.Util.Padding import unpad, pad +from base64 import b64decode, b64encode + +def decrypt(message, key, iv): + aes = None + aes = AES.new(key, AES.MODE_CBC, iv) + return unpad(aes.decrypt(b64decode(message)), AES.block_size).decode('utf-8') + +def encrypt(message, key, iv): + aes = None + aes = AES.new(key, AES.MODE_CBC, iv) + return b64encode(aes.encrypt(pad(message.encode('utf-8'), AES.block_size))).decode('utf-8') + +# create handler for each connection +async def handler(websocket): + aeskey = bytes(sys.argv[1], 'utf-8') + data = await websocket.recv() + print(data); + parsed = json.loads(data) + print(parsed['iv']) + iv = b64decode(bytes(parsed['iv'], 'utf-8')) + print(iv) + print(len(iv)) + msgType = decrypt(parsed['type'], aeskey, iv) + if msgType == "get": + search = decrypt(parsed['search'], aeskey, iv) + if search == "disks": + partitions = subprocess.check_output(["bash", "-c", "lsblk -pdo name | grep -v zram | grep -v NAME | grep -v loop | grep -v sr"]).decode("utf-8") + await websocket.send(json.dumps({"type": "response", "data": partitions})) + elif search == "diskType": + disk = decrypt(parsed['disk'], aeskey, iv) + diskType = subprocess.check_output(["bash", "-c", "lsblk -d -o rota "+disk+" | grep -v ROTA"]) + await websocket.send(json.dumps({"type": "response", "data": diskType.decode("utf-8").strip()})) + elif search == "diskSize": + disk = decrypt(parsed['disk'], aeskey, iv) + diskSize = subprocess.check_output(["bash", "-c", "lsblk -pdo size "+disk+" | grep -v SIZE"]) + await websocket.send(json.dumps({"type": "response", "data": diskSize.decode("utf-8").strip()})) + elif search == "isEfi": + if os.path.exists("/sys/firmware/efi"): + await websocket.send(json.dumps({"type": "response", "data": "UEFI"})) + else: + await websocket.send(json.dumps({"type": "response", "data": "BIOS"})) + elif search == "encryptPass": + data = decrypt(parsed['pass'], aeskey, iv) + password = subprocess.check_output(["bash", "-c", "openssl passwd -crypt "+data]) + await websocket.send(json.dumps({"type": "response", "data": password.decode("utf-8").strip()})) + elif msgType == "send": + command = decrypt(parsed['command'], aeskey, iv) + if command == "installConfig": + data = decrypt(parsed['data'], aeskey, iv) + config = open('/tmp/jade.json', 'w') + config.write(data) + config.close() + subprocess.check_output(["bash", "-c", "sudo /usr/bin/jade config /tmp/jade.json"]) + await websocket.send(json.dumps({"type": "response", "data": "Finished Installing"})) + else: + await websocket.send(json.dumps({"type": encrypt("response", aeskey, iv), "data": encrypt("Invalid Request", aeskey, iv)})) +ip = subprocess.check_output(["bash", "-c", "hostname -i | awk '{print $3}'"]).decode("utf-8").strip() +start_server = websockets.serve(handler, ip, 8080) +asyncio.get_event_loop().run_until_complete(start_server) +asyncio.get_event_loop().run_forever() +