mirror of
https://github.com/Astatin3/Polyboard.git
synced 2026-06-08 16:18:03 -06:00
Things almost done
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
Regular → Executable
+2
@@ -1,3 +1,5 @@
|
||||
data/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
|
||||
<section>
|
||||
<div class="container ubuntuMono">
|
||||
<h4 class="noselect ubuntuMono"><!--Place title here!!!--> - Log in</h4>
|
||||
<input id="usernamebox" name="username" placeholder="B0b5m1th" required>
|
||||
<label for="username" class="noselect">Username</label>
|
||||
<input type="password" class="form-control" id="passwordbox" name="password" placeholder="Password" value="" required>
|
||||
<label for="password" class="noselect">Password</label>
|
||||
|
||||
<button onclick="submit()">Log in</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
//let socket = io()
|
||||
usernamebox = document.getElementById('usernamebox')
|
||||
passwordbox = document.getElementById('passwordbox')
|
||||
|
||||
function submit(){
|
||||
authLogin(usernamebox.value, passwordbox.value)
|
||||
}
|
||||
|
||||
|
||||
// function submit() {
|
||||
// time = (new Date().getTime())
|
||||
|
||||
// hashpacket = usernamebox.value+sha256(passwordbox.value).toUpperCase()+time
|
||||
|
||||
// packet = {
|
||||
// data: sha256(hashpacket),
|
||||
// time: time
|
||||
// }
|
||||
// socket.emit('authreq', JSON.stringify(packet))
|
||||
// //alert(sha256(passwordbox.value))
|
||||
// //passwordbox.value = sha256(passwordbox.value)
|
||||
// }
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<link href="/src/pico.min.css" rel="stylesheet">
|
||||
<link href="/src/style.css" rel="stylesheet">
|
||||
|
||||
<title><!--Place title here!!!--></title>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar text-white noselect">
|
||||
<ul class="nav">
|
||||
<!--Place tabs here!!!-->
|
||||
<h2 class="navconntext text-white" id="connecticon" onclick="window.location.reload()" style="cursor:pointer;">
|
||||
Disconnected
|
||||
</h2>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="navpanel text-white noselect">
|
||||
<a href="<!--Place defaultPage here!!!-->" class="text-white navTitle">
|
||||
<!--Place title here!!!-->
|
||||
</a>
|
||||
<hr>
|
||||
<!--Place pages here!!!-->
|
||||
<hr>
|
||||
</div>
|
||||
<div class="maindiv">
|
||||
<!--Place body here!!!-->
|
||||
</div>
|
||||
<div id="popupBox" class="popupBox"> </div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
Executable
BIN
Binary file not shown.
Executable
+55
@@ -0,0 +1,55 @@
|
||||
import * as utils from '/src/utils.js'
|
||||
import * as jsonpack from '/src/jsonpack.js'
|
||||
|
||||
let c = null
|
||||
|
||||
export function authClient(rawClient) {
|
||||
c = this
|
||||
this.rawClient = rawClient
|
||||
|
||||
this.login = (username, password)=>{
|
||||
const salt = utils.getUnixTime()
|
||||
const key = (utils.sha256(
|
||||
username +
|
||||
utils.sha256(password) +
|
||||
salt
|
||||
))
|
||||
//console.log(c.rawClient.send)
|
||||
c.rawClient.send('login', {
|
||||
data: key,
|
||||
salt: salt
|
||||
})
|
||||
}
|
||||
|
||||
this.send = (data)=>{
|
||||
|
||||
}
|
||||
|
||||
this.clidata = (data)=>{
|
||||
// const session = utils.getCookie('session')
|
||||
// if(session != ''){
|
||||
// this.rawClient.send('reauth', {
|
||||
// session: session
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
this.loginSuccess = (data)=>{
|
||||
utils.popupSuccess('Connection', 'Successfully logged in!')
|
||||
utils.iconauth()
|
||||
console.log(data.data.session)
|
||||
if(data.data.session != null){
|
||||
utils.setCookie('session', data.data.session)
|
||||
}
|
||||
window.location = data.data.redir
|
||||
}
|
||||
|
||||
this.reauth = (data)=>{
|
||||
utils.popupSuccess('Connection', 'Successfully logged in!')
|
||||
utils.iconauth()
|
||||
}
|
||||
|
||||
this.rawClient.addRawTypeListener('clidata', this.clidata)
|
||||
this.rawClient.addRawTypeListener('loginSuccess', this.loginSuccess)
|
||||
this.rawClient.addRawTypeListener('reauth', this.reauth)
|
||||
}
|
||||
Executable
+559
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
Copyright (c) 2013, Rodrigo González, Sapienlab All Rights Reserved.
|
||||
Available via MIT LICENSE. See https://github.com/roro89/jsonpack/blob/master/LICENSE.md for details.
|
||||
*/
|
||||
|
||||
|
||||
var TOKEN_TRUE = -1;
|
||||
var TOKEN_FALSE = -2;
|
||||
var TOKEN_NULL = -3;
|
||||
var TOKEN_EMPTY_STRING = -4;
|
||||
var TOKEN_UNDEFINED = -5;
|
||||
|
||||
export function pack(json, options) {
|
||||
|
||||
// Canonizes the options
|
||||
options = options || {};
|
||||
|
||||
// A shorthand for debugging
|
||||
var verbose = options.verbose || false;
|
||||
|
||||
verbose && console.log('Normalize the JSON Object');
|
||||
|
||||
// JSON as Javascript Object (Not string representation)
|
||||
json = typeof json === 'string' ? this.JSON.parse(json) : json;
|
||||
|
||||
verbose && console.log('Creating a empty dictionary');
|
||||
|
||||
// The dictionary
|
||||
var dictionary = {
|
||||
strings : [],
|
||||
integers : [],
|
||||
floats : []
|
||||
};
|
||||
|
||||
verbose && console.log('Creating the AST');
|
||||
|
||||
// The AST
|
||||
var ast = (function recursiveAstBuilder(item) {
|
||||
|
||||
verbose && console.log('Calling recursiveAstBuilder with ' + this.JSON.stringify(item));
|
||||
|
||||
// The type of the item
|
||||
var type = typeof item;
|
||||
|
||||
// Case 7: The item is null
|
||||
if (item === null) {
|
||||
return {
|
||||
type : 'null',
|
||||
index : TOKEN_NULL
|
||||
};
|
||||
}
|
||||
|
||||
//add undefined
|
||||
if (typeof item === 'undefined') {
|
||||
return {
|
||||
type : 'undefined',
|
||||
index : TOKEN_UNDEFINED
|
||||
};
|
||||
}
|
||||
|
||||
// Case 1: The item is Array Object
|
||||
if ( item instanceof Array) {
|
||||
|
||||
// Create a new sub-AST of type Array (@)
|
||||
var ast = ['@'];
|
||||
|
||||
// Add each items
|
||||
for (var i in item) {
|
||||
|
||||
if (!item.hasOwnProperty(i)) continue;
|
||||
|
||||
ast.push(recursiveAstBuilder(item[i]));
|
||||
}
|
||||
|
||||
// And return
|
||||
return ast;
|
||||
|
||||
}
|
||||
|
||||
// Case 2: The item is Object
|
||||
if (type === 'object') {
|
||||
|
||||
// Create a new sub-AST of type Object ($)
|
||||
var ast = ['$'];
|
||||
|
||||
// Add each items
|
||||
for (var key in item) {
|
||||
|
||||
if (!item.hasOwnProperty(key))
|
||||
continue;
|
||||
|
||||
ast.push(recursiveAstBuilder(key));
|
||||
ast.push(recursiveAstBuilder(item[key]));
|
||||
}
|
||||
|
||||
// And return
|
||||
return ast;
|
||||
|
||||
}
|
||||
|
||||
// Case 3: The item empty string
|
||||
if (item === '') {
|
||||
return {
|
||||
type : 'empty',
|
||||
index : TOKEN_EMPTY_STRING
|
||||
};
|
||||
}
|
||||
|
||||
// Case 4: The item is String
|
||||
if (type === 'string') {
|
||||
|
||||
// The index of that word in the dictionary
|
||||
var index = _indexOf.call(dictionary.strings, item);
|
||||
|
||||
// If not, add to the dictionary and actualize the index
|
||||
if (index == -1) {
|
||||
dictionary.strings.push(_encode(item));
|
||||
index = dictionary.strings.length - 1;
|
||||
}
|
||||
|
||||
// Return the token
|
||||
return {
|
||||
type : 'strings',
|
||||
index : index
|
||||
};
|
||||
}
|
||||
|
||||
// Case 5: The item is integer
|
||||
if (type === 'number' && item % 1 === 0) {
|
||||
|
||||
// The index of that number in the dictionary
|
||||
var index = _indexOf.call(dictionary.integers, item);
|
||||
|
||||
// If not, add to the dictionary and actualize the index
|
||||
if (index == -1) {
|
||||
dictionary.integers.push(_base10To36(item));
|
||||
index = dictionary.integers.length - 1;
|
||||
}
|
||||
|
||||
// Return the token
|
||||
return {
|
||||
type : 'integers',
|
||||
index : index
|
||||
};
|
||||
}
|
||||
|
||||
// Case 6: The item is float
|
||||
if (type === 'number') {
|
||||
// The index of that number in the dictionary
|
||||
var index = _indexOf.call(dictionary.floats, item);
|
||||
|
||||
// If not, add to the dictionary and actualize the index
|
||||
if (index == -1) {
|
||||
// Float not use base 36
|
||||
dictionary.floats.push(item);
|
||||
index = dictionary.floats.length - 1;
|
||||
}
|
||||
|
||||
// Return the token
|
||||
return {
|
||||
type : 'floats',
|
||||
index : index
|
||||
};
|
||||
}
|
||||
|
||||
// Case 7: The item is boolean
|
||||
if (type === 'boolean') {
|
||||
return {
|
||||
type : 'boolean',
|
||||
index : item ? TOKEN_TRUE : TOKEN_FALSE
|
||||
};
|
||||
}
|
||||
|
||||
// Default
|
||||
throw new Error('Unexpected argument of type ' + typeof (item));
|
||||
|
||||
})(json);
|
||||
|
||||
// A set of shorthands proxies for the length of the dictionaries
|
||||
var stringLength = dictionary.strings.length;
|
||||
var integerLength = dictionary.integers.length;
|
||||
var floatLength = dictionary.floats.length;
|
||||
|
||||
verbose && console.log('Parsing the dictionary');
|
||||
|
||||
// Create a raw dictionary
|
||||
var packed = dictionary.strings.join('|');
|
||||
packed += '^' + dictionary.integers.join('|');
|
||||
packed += '^' + dictionary.floats.join('|');
|
||||
|
||||
verbose && console.log('Parsing the structure');
|
||||
|
||||
// And add the structure
|
||||
packed += '^' + (function recursiveParser(item) {
|
||||
|
||||
verbose && console.log('Calling a recursiveParser with ' + this.JSON.stringify(item));
|
||||
|
||||
// If the item is Array, then is a object of
|
||||
// type [object Object] or [object Array]
|
||||
if ( item instanceof Array) {
|
||||
|
||||
// The packed resulting
|
||||
var packed = item.shift();
|
||||
|
||||
for (var i in item) {
|
||||
|
||||
if (!item.hasOwnProperty(i))
|
||||
continue;
|
||||
|
||||
packed += recursiveParser(item[i]) + '|';
|
||||
}
|
||||
|
||||
return (packed[packed.length - 1] === '|' ? packed.slice(0, -1) : packed) + ']';
|
||||
|
||||
}
|
||||
|
||||
// A shorthand proxies
|
||||
var type = item.type, index = item.index;
|
||||
|
||||
if (type === 'strings') {
|
||||
// Just return the base 36 of index
|
||||
return _base10To36(index);
|
||||
}
|
||||
|
||||
if (type === 'integers') {
|
||||
// Return a base 36 of index plus stringLength offset
|
||||
return _base10To36(stringLength + index);
|
||||
}
|
||||
|
||||
if (type === 'floats') {
|
||||
// Return a base 36 of index plus stringLength and integerLength offset
|
||||
return _base10To36(stringLength + integerLength + index);
|
||||
}
|
||||
|
||||
if (type === 'boolean') {
|
||||
return item.index;
|
||||
}
|
||||
|
||||
if (type === 'null') {
|
||||
return TOKEN_NULL;
|
||||
}
|
||||
|
||||
if (type === 'undefined') {
|
||||
return TOKEN_UNDEFINED;
|
||||
}
|
||||
|
||||
if (type === 'empty') {
|
||||
return TOKEN_EMPTY_STRING;
|
||||
}
|
||||
|
||||
throw new TypeError('The item is alien!');
|
||||
|
||||
})(ast);
|
||||
|
||||
verbose && console.log('Ending parser');
|
||||
|
||||
// If debug, return a internal representation of dictionary and stuff
|
||||
if (options.debug)
|
||||
return {
|
||||
dictionary : dictionary,
|
||||
ast : ast,
|
||||
packed : packed
|
||||
};
|
||||
|
||||
return packed;
|
||||
|
||||
};
|
||||
|
||||
export function unpack(packed, options) {
|
||||
|
||||
// Canonizes the options
|
||||
options = options || {};
|
||||
|
||||
// A raw buffer
|
||||
var rawBuffers = packed.split('^');
|
||||
|
||||
// Create a dictionary
|
||||
options.verbose && console.log('Building dictionary');
|
||||
var dictionary = [];
|
||||
|
||||
// Add the strings values
|
||||
var buffer = rawBuffers[0];
|
||||
if (buffer !== '') {
|
||||
buffer = buffer.split('|');
|
||||
options.verbose && console.log('Parse the strings dictionary');
|
||||
for (var i=0, n=buffer.length; i<n; i++){
|
||||
dictionary.push(_decode(buffer[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the integers values
|
||||
buffer = rawBuffers[1];
|
||||
if (buffer !== '') {
|
||||
buffer = buffer.split('|');
|
||||
options.verbose && console.log('Parse the integers dictionary');
|
||||
for (var i=0, n=buffer.length; i<n; i++){
|
||||
dictionary.push(_base36To10(buffer[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the floats values
|
||||
buffer = rawBuffers[2];
|
||||
if (buffer !== '') {
|
||||
buffer = buffer.split('|')
|
||||
options.verbose && console.log('Parse the floats dictionary');
|
||||
for (var i=0, n=buffer.length; i<n; i++){
|
||||
dictionary.push(parseFloat(buffer[i]));
|
||||
}
|
||||
}
|
||||
// Free memory
|
||||
buffer = null;
|
||||
|
||||
options.verbose && console.log('Tokenizing the structure');
|
||||
|
||||
// Tokenizer the structure
|
||||
var number36 = '';
|
||||
var tokens = [];
|
||||
var len=rawBuffers[3].length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var symbol = rawBuffers[3].charAt(i);
|
||||
if (symbol === '|' || symbol === '$' || symbol === '@' || symbol === ']') {
|
||||
if (number36) {
|
||||
tokens.push(_base36To10(number36));
|
||||
number36 = '';
|
||||
}
|
||||
symbol !== '|' && tokens.push(symbol);
|
||||
} else {
|
||||
number36 += symbol;
|
||||
}
|
||||
}
|
||||
|
||||
// A shorthand proxy for tokens.length
|
||||
var tokensLength = tokens.length;
|
||||
|
||||
// The index of the next token to read
|
||||
var tokensIndex = 0;
|
||||
|
||||
options.verbose && console.log('Starting recursive parser');
|
||||
|
||||
return (function recursiveUnpackerParser() {
|
||||
|
||||
// Maybe '$' (object) or '@' (array)
|
||||
var type = tokens[tokensIndex++];
|
||||
|
||||
options.verbose && console.log('Reading collection type ' + (type === '$' ? 'object' : 'Array'));
|
||||
|
||||
// Parse an array
|
||||
if (type === '@') {
|
||||
|
||||
var node = [];
|
||||
|
||||
for (; tokensIndex < tokensLength; tokensIndex++) {
|
||||
var value = tokens[tokensIndex];
|
||||
options.verbose && console.log('Read ' + value + ' symbol');
|
||||
if (value === ']')
|
||||
return node;
|
||||
if (value === '@' || value === '$') {
|
||||
node.push(recursiveUnpackerParser());
|
||||
} else {
|
||||
switch(value) {
|
||||
case TOKEN_TRUE:
|
||||
node.push(true);
|
||||
break;
|
||||
case TOKEN_FALSE:
|
||||
node.push(false);
|
||||
break;
|
||||
case TOKEN_NULL:
|
||||
node.push(null);
|
||||
break;
|
||||
case TOKEN_UNDEFINED:
|
||||
node.push(undefined);
|
||||
break;
|
||||
case TOKEN_EMPTY_STRING:
|
||||
node.push('');
|
||||
break;
|
||||
default:
|
||||
node.push(dictionary[value]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
options.verbose && console.log('Parsed ' + this.JSON.stringify(node));
|
||||
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
// Parse a object
|
||||
if (type === '$') {
|
||||
var node = {};
|
||||
|
||||
for (; tokensIndex < tokensLength; tokensIndex++) {
|
||||
|
||||
var key = tokens[tokensIndex];
|
||||
|
||||
if (key === ']')
|
||||
return node;
|
||||
|
||||
if (key === TOKEN_EMPTY_STRING)
|
||||
key = '';
|
||||
else
|
||||
key = dictionary[key];
|
||||
|
||||
var value = tokens[++tokensIndex];
|
||||
|
||||
if (value === '@' || value === '$') {
|
||||
node[key] = recursiveUnpackerParser();
|
||||
} else {
|
||||
switch(value) {
|
||||
case TOKEN_TRUE:
|
||||
node[key] = true;
|
||||
break;
|
||||
case TOKEN_FALSE:
|
||||
node[key] = false;
|
||||
break;
|
||||
case TOKEN_NULL:
|
||||
node[key] = null;
|
||||
break;
|
||||
case TOKEN_UNDEFINED:
|
||||
node[key] = undefined;
|
||||
break;
|
||||
case TOKEN_EMPTY_STRING:
|
||||
node[key] = '';
|
||||
break;
|
||||
default:
|
||||
node[key] = dictionary[value];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
options.verbose && console.log('Parsed ' + this.JSON.stringify(node));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
throw new TypeError('Bad token ' + type + ' isn\'t a type');
|
||||
|
||||
})();
|
||||
|
||||
}
|
||||
/**
|
||||
* Get the index value of the dictionary
|
||||
* @param {Object} dictionary a object that have two array attributes: 'string' and 'number'
|
||||
* @param {Object} data
|
||||
*/
|
||||
var _indexOfDictionary = function(dictionary, value) {
|
||||
|
||||
// The type of the value
|
||||
var type = typeof value;
|
||||
|
||||
// If is boolean, return a boolean token
|
||||
if (type === 'boolean')
|
||||
return value ? TOKEN_TRUE : TOKEN_FALSE;
|
||||
|
||||
// If is null, return a... yes! the null token
|
||||
if (value === null)
|
||||
return TOKEN_NULL;
|
||||
|
||||
//add undefined
|
||||
if (typeof value === 'undefined')
|
||||
return TOKEN_UNDEFINED;
|
||||
|
||||
|
||||
if (value === '') {
|
||||
return TOKEN_EMPTY_STRING;
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
value = _encode(value);
|
||||
var index = _indexOf.call(dictionary.strings, value);
|
||||
if (index === -1) {
|
||||
dictionary.strings.push(value);
|
||||
index = dictionary.strings.length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If has an invalid JSON type (example a function)
|
||||
if (type !== 'string' && type !== 'number') {
|
||||
throw new Error('The type is not a JSON type');
|
||||
};
|
||||
|
||||
if (type === 'string') {// string
|
||||
value = _encode(value);
|
||||
} else if (value % 1 === 0) {// integer
|
||||
value = _base10To36(value);
|
||||
} else {// float
|
||||
|
||||
}
|
||||
|
||||
// If is number, "serialize" the value
|
||||
value = type === 'number' ? _base10To36(value) : _encode(value);
|
||||
|
||||
// Retrieve the index of that value in the dictionary
|
||||
var index = _indexOf.call(dictionary[type], value);
|
||||
|
||||
// If that value is not in the dictionary
|
||||
if (index === -1) {
|
||||
// Push the value
|
||||
dictionary[type].push(value);
|
||||
// And return their index
|
||||
index = dictionary[type].length - 1;
|
||||
}
|
||||
|
||||
// If the type is a number, then add the '+' prefix character
|
||||
// to differentiate that they is a number index. If not, then
|
||||
// just return a 36-based representation of the index
|
||||
return type === 'number' ? '+' + index : index;
|
||||
|
||||
};
|
||||
|
||||
var _encode = function(str) {
|
||||
if ( typeof str !== 'string')
|
||||
return str;
|
||||
|
||||
return str.replace(/[\+ \|\^\%]/g, function(a) {
|
||||
return ({
|
||||
' ' : '+',
|
||||
'+' : '%2B',
|
||||
'|' : '%7C',
|
||||
'^' : '%5E',
|
||||
'%' : '%25'
|
||||
})[a]
|
||||
});
|
||||
};
|
||||
|
||||
var _decode = function(str) {
|
||||
if ( typeof str !== 'string')
|
||||
return str;
|
||||
|
||||
return str.replace(/\+|%2B|%7C|%5E|%25/g, function(a) {
|
||||
return ({
|
||||
'+' : ' ',
|
||||
'%2B' : '+',
|
||||
'%7C' : '|',
|
||||
'%5E' : '^',
|
||||
'%25' : '%'
|
||||
})[a]
|
||||
})
|
||||
};
|
||||
|
||||
var _base10To36 = function(number) {
|
||||
return Number.prototype.toString.call(number, 36).toUpperCase();
|
||||
};
|
||||
|
||||
var _base36To10 = function(number) {
|
||||
return parseInt(number, 36);
|
||||
};
|
||||
|
||||
var _indexOf = Array.prototype.indexOf ||
|
||||
function(obj, start) {
|
||||
for (var i = (start || 0), j = this.length; i < j; i++) {
|
||||
if (this[i] === obj) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
import * as utils from "/src/utils.js"
|
||||
import * as packets from "/src/packets.js"
|
||||
import * as auth from "/src/auth.js"
|
||||
import * as jsonpack from "/src/jsonpack.js"
|
||||
|
||||
const hostname = window.location.href.split('/')[2]
|
||||
|
||||
//import * as crypto from "/src/crypto-js.min.js"
|
||||
|
||||
let rawClient = new packets.rawClient('/listen')
|
||||
let client = new auth.authClient(rawClient)
|
||||
|
||||
window.utils = utils
|
||||
window.packets = packets
|
||||
window.auth = auth
|
||||
window.jsonpack = jsonpack
|
||||
|
||||
window.rawClient = rawClient
|
||||
window.authClient = client
|
||||
|
||||
window.authLogin = client.login
|
||||
window.sendAuth = client.send
|
||||
window.sendRaw = rawClient.send
|
||||
window.addRawTypeListener = rawClient.addRawTypeListener
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
import * as jsonpack from '/src/jsonpack.js'
|
||||
import * as utils from "/src/utils.js"
|
||||
|
||||
|
||||
let cID = null
|
||||
let evListeners = []
|
||||
let c = null
|
||||
|
||||
function getErrorDesc(error){
|
||||
switch(error){
|
||||
case 'invalidLogin':
|
||||
return 'Invalid username or password'
|
||||
case 'invalidLoginRequest':
|
||||
return 'Some part of the login request is invalid, please try again'
|
||||
case 'prelogin':
|
||||
return 'You are already logged in'
|
||||
}
|
||||
return error
|
||||
}
|
||||
|
||||
function processData(data){
|
||||
data = jsonpack.unpack(data)
|
||||
switch(data.type){
|
||||
case 'clidata':
|
||||
c.cID = data.data.cid
|
||||
if(window.location.pathname == "/login") {
|
||||
utils.popupWarning('Connection', 'Connected to server!')
|
||||
utils.iconunauth()
|
||||
}else{
|
||||
utils.popupInfo('Connection', 'Connected to server!')
|
||||
utils.iconauth()
|
||||
}
|
||||
break
|
||||
case 'data':
|
||||
console.log(data.data)
|
||||
break
|
||||
case 'error':
|
||||
utils.popupError(`Error: ${data.data}`, getErrorDesc(data.data))
|
||||
}
|
||||
for(let i=0;i<evListeners.length;i++){
|
||||
const ev = evListeners[i]
|
||||
if(ev.type == data.type){
|
||||
ev.func(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function rawClient(loc) {
|
||||
c = this
|
||||
this.connected = false
|
||||
this.location = loc
|
||||
this.evtSource = new EventSource(loc);
|
||||
|
||||
this.onopen = ()=>{}
|
||||
this.onclose = ()=>{}
|
||||
|
||||
this.cID = null
|
||||
|
||||
this.evtSource.onmessage = (event) => {
|
||||
console.log(`Data: ${event.data}`)
|
||||
processData(event.data)
|
||||
}
|
||||
|
||||
this.evtSource.onerror = (event) => {
|
||||
console.log('Error!')
|
||||
this.connected = false
|
||||
utils.icondisconnect()
|
||||
utils.popupError('Connection', 'Disconnected from server')
|
||||
this.onclose()
|
||||
}
|
||||
|
||||
this.evtSource.onopen = (event) => {
|
||||
console.log('Connected!')
|
||||
this.connected = true
|
||||
utils.iconunauth()
|
||||
this.onopen()
|
||||
}
|
||||
|
||||
this.send = (type, data)=>{
|
||||
//console.log({type, data})
|
||||
fetch(this.location, {
|
||||
method: "post",
|
||||
headers: {
|
||||
'Accept': '*',
|
||||
'Content-Type': 'text/plain'
|
||||
},
|
||||
body: jsonpack.pack({
|
||||
type: type,
|
||||
data: data,
|
||||
cid: this.cID
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.addRawTypeListener = (type, func)=>{
|
||||
evListeners.push({
|
||||
type: type,
|
||||
func: func
|
||||
})
|
||||
}
|
||||
}
|
||||
Vendored
+5
File diff suppressed because one or more lines are too long
Executable
+270
@@ -0,0 +1,270 @@
|
||||
@font-face {
|
||||
font-family: "UbuntuMono";
|
||||
src: url(/src/UbuntuMono-R.ttf) format("truetype");
|
||||
}
|
||||
|
||||
:root {
|
||||
--topnavheight: 45px;
|
||||
--sidenavwidth: 150px;
|
||||
--popupBoxWidth: 300px;
|
||||
|
||||
--background-0: #212529;
|
||||
--background-1: #404040;
|
||||
|
||||
--text-0: #242424;
|
||||
--text-1: #d3d3d3;
|
||||
|
||||
--text-black: #242424;
|
||||
--text-white: #d3d3d3;
|
||||
|
||||
--success-1:#059100;
|
||||
--warning-1:#ffdc3e;
|
||||
--error-1: #b60f0f;
|
||||
|
||||
--font: "UbuntuMono";
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: var(--font);
|
||||
}
|
||||
|
||||
body {
|
||||
position:fixed;
|
||||
right: 0;
|
||||
height: 0;
|
||||
overflow: scroll;
|
||||
width: calc( 100vw - var(--sidenavwidth) + 32px );
|
||||
height: calc( 100vh - var(--topnavheight) + 32px );
|
||||
}
|
||||
|
||||
.maindiv {
|
||||
position: fixed;
|
||||
top: var(--topnavheight);
|
||||
left: var(--sidenavwidth);
|
||||
width: calc(100% - var(--sidenavwidth));
|
||||
height: calc(100% - var(--topnavheight));
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
color: var(--text-white) !important;
|
||||
font-family: var(--font);
|
||||
}
|
||||
|
||||
.text-black {
|
||||
color: var(--text-black) !important;
|
||||
font-family: var(--font);
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
height:var(--topnavheight);
|
||||
padding:5px;
|
||||
padding-left: calc(var(--sidenavwidth) + 10px);
|
||||
z-index: 10;
|
||||
background-color: var(--background-0);
|
||||
}
|
||||
|
||||
.navbar ul {
|
||||
position: fixed;
|
||||
top: calc(var(--topnavheight) * 0.1);
|
||||
padding: 0px;
|
||||
/* height: var(--topnavheight); */
|
||||
height: calc(var(--topnavheight) * 0.8);
|
||||
padding-right: calc(var(--topnavheight) * 2.8);
|
||||
left: var(--sidenavwidth);
|
||||
width: calc(100% - var(--sidenavwidth));
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.navpanel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
padding-left: 14px;
|
||||
|
||||
|
||||
position:fixed;
|
||||
top: var(--topnavheight);
|
||||
bottom:0;
|
||||
left:0;
|
||||
width: var(--sidenavwidth);
|
||||
height:100%;
|
||||
resize: left;
|
||||
z-index: 20;
|
||||
background-color: var(--background-0);
|
||||
}
|
||||
|
||||
.navpanel details {
|
||||
padding-bottom: 0px;
|
||||
padding-left: 5px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.navpanel summary {
|
||||
font-size: 15px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.navpanel summary:hover {
|
||||
color: var(--primary);
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
}
|
||||
.navpanel ul {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.navpanel li {
|
||||
font-size: 14px;
|
||||
margin-left: -5px;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navpanel li:hover {
|
||||
color: var(--primary);
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
li::marker {
|
||||
content: "- ";
|
||||
padding-left: -20px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
.navconntext {
|
||||
position: fixed;
|
||||
font-size: calc(var(--topnavheight) * 0.3);
|
||||
margin: calc((var(--topnavheight) * 0.25 * 0.75) - 5px);
|
||||
background-color: rgba(255, 0, 0, 0.2);
|
||||
border-style: solid;
|
||||
border-width: 1px !important;
|
||||
border-color: #ff0000;
|
||||
padding: 8px;
|
||||
right:0;
|
||||
top:0;
|
||||
}
|
||||
|
||||
.sidenav-button {
|
||||
width:100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.topnav-button {
|
||||
margin-left: 13px !important;
|
||||
height: 100% !important;
|
||||
|
||||
font-size: calc( var(--topnavheight) * ( 3 / 9 ) );
|
||||
line-height: calc( var(--topnavheight) * ( 3 / 9 ) );
|
||||
|
||||
}
|
||||
|
||||
.navTitle {
|
||||
position: fixed;
|
||||
margin-bottom: 1px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: var(--sidenavwidth);
|
||||
height: var(--topnavheight);
|
||||
|
||||
text-align:center;
|
||||
line-height: var(--topnavheight);
|
||||
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.navTitle:hover {
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
|
||||
.popupBox {
|
||||
position: fixed;
|
||||
|
||||
/* display: flex;
|
||||
box-sizing: content-box;
|
||||
justify-content: flex-end;
|
||||
flex-direction: column; */
|
||||
|
||||
width: var(--popupBoxWidth);
|
||||
height: calc(100% - var(--topnavheight) - 10px);
|
||||
bottom: 0;
|
||||
right: 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.popupBox dialog {
|
||||
display: flex;
|
||||
z-index: inherit;
|
||||
position: relative;
|
||||
top: inherit;
|
||||
right: inherit;
|
||||
bottom: inherit;
|
||||
left: inherit;
|
||||
align-items: inherit;
|
||||
justify-content: inherit;
|
||||
width: inherit;
|
||||
min-width: inherit;
|
||||
height: auto;
|
||||
min-height: inherit;
|
||||
padding: 0;
|
||||
background-color: inherit;
|
||||
|
||||
left: 0;
|
||||
margin: 0;
|
||||
margin-bottom: 20px;
|
||||
|
||||
justify-content:right;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.popupBox article {
|
||||
margin: 0;
|
||||
opacity: 0.5;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.popupBox header {
|
||||
border: 0;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.popupBox article:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.popupBox article a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.popupBox article p {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Old versions of Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome, Edge, Opera and Firefox */
|
||||
}
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
export const setCookie = (name, value, hours = 1, path = '/') => {
|
||||
const expires = new Date(Date.now() + hours * 6e4).toUTCString()
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; path=${path}; SameSite=None; secure=True; session=True`
|
||||
}
|
||||
|
||||
export const getCookie = (name) => {
|
||||
return document.cookie.split('; ').reduce((r, v) => {
|
||||
const parts = v.split('=')
|
||||
return parts[0] === name ? decodeURIComponent(parts[1]) : r
|
||||
}, '')
|
||||
}
|
||||
|
||||
export function genID(length = 8){
|
||||
// Declare all characters
|
||||
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
// Pick characers randomly
|
||||
let str = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
str += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
export function icondisconnect() {
|
||||
let icon = document.getElementById('connecticon')
|
||||
icon.style.backgroundColor = "rgba(255, 0, 0, 0.2)"
|
||||
icon.style.borderColor = "#ff0000"
|
||||
icon.innerText = "Disconnected"
|
||||
}
|
||||
|
||||
export function iconunauth() {
|
||||
let icon = document.getElementById('connecticon')
|
||||
icon.style.backgroundColor = "rgba(255, 255, 0, 0.2)"
|
||||
icon.style.borderColor = "#ffff00"
|
||||
icon.innerText = "Unauthenticated"
|
||||
}
|
||||
|
||||
export function iconauth() {
|
||||
let icon = document.getElementById('connecticon')
|
||||
icon.style.backgroundColor = "rgba(0, 255, 0, 0.2)"
|
||||
icon.style.borderColor = "#00ff00"
|
||||
icon.innerText = "Authenticated"
|
||||
}
|
||||
|
||||
// function addPopup(bgcolor, fgcolor, innerHTML) {
|
||||
// const elem = document.getElementById('popupBox')
|
||||
// const id = 'popup-'+genID(16)
|
||||
// elem.innerHTML = `<div class="popup"
|
||||
// class="popup"
|
||||
// style="background-color: ${bgcolor};
|
||||
// color: ${fgcolor}"
|
||||
// id='${id}'
|
||||
// onclick="elem=document.getElementById('${id}');elem.parentNode.removeChild(elem)">
|
||||
// ${innerHTML}
|
||||
// </div>` +
|
||||
// elem.innerHTML
|
||||
|
||||
// setTimeout(()=>{
|
||||
// elem.parentNode.removeChild(elem)
|
||||
// }, 30000)
|
||||
|
||||
// }
|
||||
|
||||
function addPopup(bgcolor, isDark, title, content) {
|
||||
const elem = document.getElementById('popupBox')
|
||||
let header
|
||||
let textColor
|
||||
if(isDark){
|
||||
header = 'rgba(255,255,255,0.05)'
|
||||
textColor = 'text-white'
|
||||
}else{
|
||||
header = 'rgba(0,0,0,0.2)'
|
||||
textColor = 'text-black'
|
||||
}
|
||||
elem.innerHTML += `
|
||||
<dialog class="example ${textColor}" open>
|
||||
<article style="background-color:${bgcolor};">
|
||||
<header style="background-color:${header};">
|
||||
<p class='noselect' style='font-size:10px;opacity:0.75'>${formatTime(getUnixTime())}</p>
|
||||
<a class="close ${textColor}" onclick="elem=document.getElementById('popupBox');elem.removeChild(this.parentElement.parentElement.parentElement)"></a>
|
||||
${title}
|
||||
</header>
|
||||
<p class='${textColor}'>${content}</p>
|
||||
</article>
|
||||
</dialog>
|
||||
`
|
||||
}
|
||||
|
||||
export function popupInfo(title, text){
|
||||
addPopup('var(--card-sectionning-background-color)', true, title, text)
|
||||
}
|
||||
|
||||
export function popupSuccess(title, text){
|
||||
addPopup('#005000', true, title, text)
|
||||
}
|
||||
|
||||
export function popupWarning(title, text){
|
||||
addPopup('#393900', true, title, text)
|
||||
}
|
||||
|
||||
export function popupError(title, text){
|
||||
addPopup('#500000', true, title, text)
|
||||
}
|
||||
|
||||
export function getUnixTime() {
|
||||
return (+ new Date())
|
||||
}
|
||||
|
||||
function formatTime(Millis){
|
||||
const date = new Date(Millis)
|
||||
|
||||
if(date.getDate() != (new Date()).getDate()){
|
||||
return date.getMonth()+1 + "/" + date.getDate() + "/" + date.getFullYear()
|
||||
}else{
|
||||
var Hour = ""
|
||||
var Minute = ""
|
||||
var AmPm = ""
|
||||
|
||||
if(date.getHours() == 0){
|
||||
Hour = "12"
|
||||
AmPm = "AM"
|
||||
}else if(date.getHours() < 12){
|
||||
Hour = date.getHours()
|
||||
AmPm = "AM"
|
||||
}else if(date.getHours() == 12){
|
||||
Hour = "12"
|
||||
AmPm = "PM"
|
||||
}else{
|
||||
Hour = date.getHours() - 12
|
||||
AmPm = "PM"
|
||||
}
|
||||
|
||||
if(date.getMinutes() < 10){
|
||||
Minute = "0" + date.getMinutes()
|
||||
}else{
|
||||
Minute = date.getMinutes()
|
||||
}
|
||||
|
||||
return Hour + ":" + Minute + " " + AmPm
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export function sha256(ascii) {
|
||||
function rightRotate(value, amount) {
|
||||
return (value>>>amount) | (value<<(32 - amount));
|
||||
};
|
||||
|
||||
var mathPow = Math.pow;
|
||||
var maxWord = mathPow(2, 32);
|
||||
var lengthProperty = 'length'
|
||||
var i, j; // Used as a counter across the whole file
|
||||
var result = ''
|
||||
|
||||
var words = [];
|
||||
var asciiBitLength = ascii[lengthProperty]*8;
|
||||
|
||||
//* caching results is optional - remove/add slash from front of this line to toggle
|
||||
// Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes
|
||||
// (we actually calculate the first 64, but extra values are just ignored)
|
||||
var hash = sha256.h = sha256.h || [];
|
||||
// Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes
|
||||
var k = sha256.k = sha256.k || [];
|
||||
var primeCounter = k[lengthProperty];
|
||||
/*/
|
||||
var hash = [], k = [];
|
||||
var primeCounter = 0;
|
||||
//*/
|
||||
|
||||
var isComposite = {};
|
||||
for (var candidate = 2; primeCounter < 64; candidate++) {
|
||||
if (!isComposite[candidate]) {
|
||||
for (i = 0; i < 313; i += candidate) {
|
||||
isComposite[i] = candidate;
|
||||
}
|
||||
hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0;
|
||||
k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0;
|
||||
}
|
||||
}
|
||||
|
||||
ascii += '\x80' // Append Ƈ' bit (plus zero padding)
|
||||
while (ascii[lengthProperty]%64 - 56) ascii += '\x00' // More zero padding
|
||||
for (i = 0; i < ascii[lengthProperty]; i++) {
|
||||
j = ascii.charCodeAt(i);
|
||||
if (j>>8) return; // ASCII check: only accept characters in range 0-255
|
||||
words[i>>2] |= j << ((3 - i)%4)*8;
|
||||
}
|
||||
words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0);
|
||||
words[words[lengthProperty]] = (asciiBitLength)
|
||||
|
||||
// process each chunk
|
||||
for (j = 0; j < words[lengthProperty];) {
|
||||
var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration
|
||||
var oldHash = hash;
|
||||
// This is now the undefinedworking hash", often labelled as variables a...g
|
||||
// (we have to truncate as well, otherwise extra entries at the end accumulate
|
||||
hash = hash.slice(0, 8);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
var i2 = i + j;
|
||||
// Expand the message into 64 words
|
||||
// Used below if
|
||||
var w15 = w[i - 15], w2 = w[i - 2];
|
||||
|
||||
// Iterate
|
||||
var a = hash[0], e = hash[4];
|
||||
var temp1 = hash[7]
|
||||
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1
|
||||
+ ((e&hash[5])^((~e)&hash[6])) // ch
|
||||
+ k[i]
|
||||
// Expand the message schedule if needed
|
||||
+ (w[i] = (i < 16) ? w[i] : (
|
||||
w[i - 16]
|
||||
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0
|
||||
+ w[i - 7]
|
||||
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1
|
||||
)|0
|
||||
);
|
||||
// This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble
|
||||
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0
|
||||
+ ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj
|
||||
|
||||
hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice()
|
||||
hash[4] = (hash[4] + temp1)|0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
hash[i] = (hash[i] + oldHash[i])|0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 3; j + 1; j--) {
|
||||
var b = (hash[i]>>(j*8))&255;
|
||||
result += ((b < 16) ? 0 : '') + b.toString(16);
|
||||
}
|
||||
}
|
||||
return result.toUpperCase();
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
import src.web as web
|
||||
import src.utils as utils
|
||||
import src.modules as modules
|
||||
from sys import argv
|
||||
|
||||
webserv = web.webserv()
|
||||
moduleMaster = modules.moduleMaster()
|
||||
|
||||
def main():
|
||||
|
||||
if not utils.pathExists('data'):
|
||||
utils.makeDir('data')
|
||||
|
||||
if not utils.pathExists('data/creds.json'):
|
||||
if input("No credentials file was found, \nwould you like to create one? (Y/n): ").lower() in ["yes", "y", ""]:
|
||||
utils.writeFile('data/creds.json', utils.genDefaultAccounts())
|
||||
|
||||
if webserv.secure and ( not utils.pathExists('data/selfsign.crt') or not utils.pathExists('data/selfsign.key') ):
|
||||
if input("No ssl key/cert was found, \nwould you like to create them? (Y/n): ").lower() in ["yes", "y", ""]:
|
||||
|
||||
if not utils.pathExists('data/selfsign.key'):
|
||||
utils.genKey(utils.getRoot('data/'))
|
||||
|
||||
if not utils.pathExists('data/selfsign.crt'):
|
||||
utils.genCert(utils.getRoot('data/'))
|
||||
|
||||
moduleMaster.initModules(webserv)
|
||||
|
||||
webserv.start()
|
||||
moduleMaster.runModules()
|
||||
|
||||
# for m in modules:
|
||||
# m.module.main()
|
||||
|
||||
def printHelp():
|
||||
print("""
|
||||
Modulator usage:
|
||||
|
||||
-h -? --help - Print this help information
|
||||
-v --verbose - Print verbose information, default: false
|
||||
-u --unsecure - Use http instead of https, default: false
|
||||
-p --port <int> - Set port of the webserver, default: 80 or 44
|
||||
-h --host <str> - Set host of the webserver, default: 0.0.0.0
|
||||
|
||||
--defaultTab <str> - Set the default tab on visit, default: 'main'
|
||||
--title <str> - Set the title of the html pages, default: 'Modulator'
|
||||
|
||||
Examples:
|
||||
|
||||
$ python3 ./main.py
|
||||
$ python3 ./main.py -vo 127.0.0.1
|
||||
$ python3 ./main.py -p 12345 -h 192.168.0.123
|
||||
""")
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
i = 1
|
||||
|
||||
while i < len(argv):
|
||||
arg = argv[i]
|
||||
sargs = list(arg)
|
||||
if sargs[0] == '-' and sargs[1] == '-':
|
||||
match arg:
|
||||
case '--help':
|
||||
printHelp()
|
||||
exit()
|
||||
case '--verbose':
|
||||
webserv.verbose = True
|
||||
i+=1; continue
|
||||
case '--unsecure':
|
||||
webserv.secure = False
|
||||
if webserv.port == 443:
|
||||
webserv.port = 80
|
||||
i+=1; continue
|
||||
case '--port':
|
||||
webserv.port = int(argv[i+1])
|
||||
i+=2; continue
|
||||
case '--host':
|
||||
webserv.host = str(argv[i+1])
|
||||
i+=2; continue
|
||||
case '--defaultTab':
|
||||
webserv.defaultTab = str(argv[i+1])
|
||||
i+=2; continue
|
||||
case '--title':
|
||||
webserv.title = str(argv[i+1])
|
||||
i+=2; continue
|
||||
elif sargs[0] == '-':
|
||||
for sarg in sargs:
|
||||
match sarg:
|
||||
case 'h' | '?':
|
||||
printHelp()
|
||||
exit()
|
||||
case 'v':
|
||||
webserv.verbose = True
|
||||
case 'u':
|
||||
webserv.secure = False
|
||||
if webserv.port == 443:
|
||||
webserv.port = 80
|
||||
case 'p':
|
||||
webserv.port = int(argv[i+1])
|
||||
i+=1; continue
|
||||
case 'o':
|
||||
webserv.host = str(argv[i+1])
|
||||
i+=1; continue
|
||||
|
||||
i += 1
|
||||
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
<p>Test123123!</p>
|
||||
@@ -0,0 +1,11 @@
|
||||
<p>Test!</p>
|
||||
<button id="testButton" onclick="testFunc()">test!</button>
|
||||
<script>
|
||||
function getel(el) {return document.getElementById(el)}
|
||||
|
||||
function testFunc() {
|
||||
window.sendRaw('test1', {
|
||||
data: 'test!'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
from modules.main import test as test
|
||||
|
||||
def test1(ac, data):
|
||||
print(ac)
|
||||
print(data)
|
||||
|
||||
def main(mm):
|
||||
# mm.addAuthEventListener('test1', test1)
|
||||
print(mm.rawServer.addEventListener('test1', test1))
|
||||
#mm.rawServer.addEventListener('login', test1)
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "main",
|
||||
"creators": ["ASTATIN3"],
|
||||
"version": "1.0",
|
||||
"entrypoint": "modules/main/main.py",
|
||||
"tabs": [
|
||||
{
|
||||
"name": "main",
|
||||
"defaultPage": "dashboard",
|
||||
"pages": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "dashboard",
|
||||
"location": "modules/main/Dashboard.html"
|
||||
},
|
||||
{
|
||||
"type": "folder",
|
||||
"name": "folder 1",
|
||||
"pages": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "dashboardcopy",
|
||||
"location": "modules/main/Dashboard-copy.html"
|
||||
},
|
||||
{
|
||||
"type": "folder",
|
||||
"name": "folder 2",
|
||||
"pages": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "dashboardcopy",
|
||||
"location": "modules/main/Dashboard-copy.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
def test():
|
||||
return "testsjdhgkjrhsgkhjertestsjdhgkjrhsgkhjertestsjdhgkjrhsgkhjer"
|
||||
Executable
+142
@@ -0,0 +1,142 @@
|
||||
import os
|
||||
import base64
|
||||
import json
|
||||
from hashlib import sha256
|
||||
from flask import Flask, render_template, Response
|
||||
from flask import request, redirect, url_for, make_response
|
||||
|
||||
import src.packets as packets
|
||||
import src.utils as utils
|
||||
|
||||
class authUser:
|
||||
def __init__(self):
|
||||
self.username = None
|
||||
self.permGroups = []
|
||||
self.sha256passwordhash = None
|
||||
|
||||
class authClient:
|
||||
def __init__(self):
|
||||
self.username = None
|
||||
self.session = utils.randID(32)
|
||||
self.userData = None
|
||||
|
||||
self.timeout = utils.getUnixTime() + (60 * 60 * 1000)
|
||||
self.loginTime = utils.getUnixTime()
|
||||
self.lastReauth = utils.getUnixTime()
|
||||
|
||||
self.rawClient = None
|
||||
def send(type, data):
|
||||
self.rawClient.send(type, data)
|
||||
|
||||
class authServer:
|
||||
def __init__(self):
|
||||
self.rawServer = None
|
||||
self.app = None
|
||||
self.clients = []
|
||||
self.users = []
|
||||
|
||||
def login(self, c, data):
|
||||
if c.clientid != data['cid']:
|
||||
c.send('error', 'invalidLoginRequest')
|
||||
return
|
||||
|
||||
if int(data['data']['salt']) > (utils.getUnixTime() + 5000):
|
||||
c.send('error', 'invalidLoginRequest')
|
||||
return
|
||||
|
||||
logins = json.loads(utils.readFile(utils.getRoot('data/')+'creds.json'))
|
||||
isValid = False
|
||||
validAcc = None
|
||||
for acc in self.users:
|
||||
hash = utils.hash(str(acc.username)+str(acc.sha256passwordhash)+str(data['data']['salt']))
|
||||
if hash == str(data['data']['data']):
|
||||
isValid = True
|
||||
validAcc = acc
|
||||
break
|
||||
|
||||
if isValid:
|
||||
if utils.getatribinarr(self.clients, 'rawClient', c):
|
||||
c.send('error', 'prelogin')
|
||||
return
|
||||
|
||||
ac = authClient()
|
||||
ac.username = validAcc.username
|
||||
ac.userData = validAcc
|
||||
ac.rawClient = c
|
||||
|
||||
self.clients.append(ac)
|
||||
|
||||
c.send('loginSuccess', {
|
||||
'username': ac.username,
|
||||
'session': ac.session,
|
||||
'redir': f'/{self.app.defaultTab}/{self.app.defaultPage}',
|
||||
'timeout': ac.timeout
|
||||
})
|
||||
|
||||
return
|
||||
else:
|
||||
c.send('error', 'invalidLogin')
|
||||
return
|
||||
|
||||
def reauth(self, c, data):
|
||||
session = data['data']['session']
|
||||
ac = utils.getatribinarr(self.clients, 'session', session)
|
||||
|
||||
if ac == None:
|
||||
c.send('error', 'invalidLoginRequest')
|
||||
return
|
||||
if ac.rawClient.address != request.remote_addr:
|
||||
c.send('error', 'invalidLoginRequest')
|
||||
return
|
||||
if utils.getUnixTime() > ac.timeout:
|
||||
c.send('error', 'invalidLoginRequest')
|
||||
return
|
||||
|
||||
ac.rawClient = c
|
||||
ac.lastReauth = utils.getUnixTime()
|
||||
|
||||
c.send('reauth', {
|
||||
'username': ac.username
|
||||
})
|
||||
|
||||
def cookieLogin(self, request):
|
||||
session = request.cookies.get('session')
|
||||
if session == None:
|
||||
return None
|
||||
|
||||
ac = utils.getatribinarr(self.clients, 'session', session)
|
||||
|
||||
if ac == None:
|
||||
return False
|
||||
if ac.rawClient.address != request.remote_addr:
|
||||
return False
|
||||
if utils.getUnixTime() > ac.timeout:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def initRawServer(self):
|
||||
self.app.rawServer.addEventListener('login', self.login)
|
||||
self.app.rawServer.addEventListener('reauth', self.reauth)
|
||||
|
||||
def reloadUsers(self):
|
||||
data = json.loads(utils.readFile(utils.getRoot('data/')+'creds.json'))
|
||||
|
||||
self.users = []
|
||||
|
||||
for acc in data:
|
||||
user = authUser()
|
||||
user.username = acc['username']
|
||||
user.sha256passwordhash = acc['sha256passwordhash']
|
||||
user.permGroups = acc['permGroups']
|
||||
self.users.append(user)
|
||||
|
||||
def startAuthListener(app):
|
||||
global authServer
|
||||
authServer = authServer()
|
||||
authServer.app = app
|
||||
authServer.reloadUsers()
|
||||
packets.startRawListener(app)
|
||||
authServer.initRawServer()
|
||||
|
||||
return authServer
|
||||
Executable
+273
@@ -0,0 +1,273 @@
|
||||
# Copyright (c) 2013, Rodrigo González, Sapienlab All Rights Reserved.
|
||||
# Available via MIT LICENSE. See https://github.com/roro89/jsonpack/blob/master/LICENSE.md for details.
|
||||
|
||||
TOKEN_TRUE = -1
|
||||
TOKEN_FALSE = -2
|
||||
TOKEN_NULL = -3
|
||||
TOKEN_EMPTY_STRING = -4
|
||||
TOKEN_UNDEFINED = -5
|
||||
|
||||
def pack(json):
|
||||
json = json if isinstance(json, str) else json
|
||||
dictionary = {
|
||||
'strings': [],
|
||||
'integers': [],
|
||||
'floats': []
|
||||
}
|
||||
|
||||
def recursiveAstBuilder(item):
|
||||
if item is None:
|
||||
return {
|
||||
'type': 'null',
|
||||
'index': TOKEN_NULL
|
||||
}
|
||||
if item == '':
|
||||
return {
|
||||
'type': '',
|
||||
'index': TOKEN_UNDEFINED
|
||||
}
|
||||
if isinstance(item, list):
|
||||
ast = ['@']
|
||||
for i in item:
|
||||
ast.append(recursiveAstBuilder(i))
|
||||
return ast
|
||||
if isinstance(item, dict):
|
||||
ast = ['$']
|
||||
for key, value in item.items():
|
||||
ast.append(recursiveAstBuilder(key))
|
||||
ast.append(recursiveAstBuilder(value))
|
||||
return ast
|
||||
if item == '':
|
||||
return {
|
||||
'type': 'empty',
|
||||
'index': TOKEN_EMPTY_STRING
|
||||
}
|
||||
if type(item) == type(True):
|
||||
return {
|
||||
'type': 'boolean',
|
||||
'index': TOKEN_TRUE if item else TOKEN_FALSE
|
||||
}
|
||||
if isinstance(item, str):
|
||||
index = dictionary['strings'].index(item) if item in dictionary['strings'] else -1
|
||||
if index == -1:
|
||||
dictionary['strings'].append((item))
|
||||
index = len(dictionary['strings']) - 1
|
||||
return {
|
||||
'type': 'strings',
|
||||
'index': index
|
||||
}
|
||||
if isinstance(item, int):
|
||||
index = dictionary['integers'].index(item) if item in dictionary['integers'] else -1
|
||||
if index == -1:
|
||||
dictionary['integers'].append(_base10To36(item))
|
||||
index = len(dictionary['integers']) - 1
|
||||
return {
|
||||
'type': 'integers',
|
||||
'index': index
|
||||
}
|
||||
if isinstance(item, float):
|
||||
index = dictionary['floats'].index(item) if item in dictionary['floats'] else -1
|
||||
if index == -1:
|
||||
dictionary['floats'].append(item)
|
||||
index = len(dictionary['floats']) - 1
|
||||
return {
|
||||
'type': 'floats',
|
||||
'index': index
|
||||
}
|
||||
raise TypeError('Unexpected argument of type ' + str(type(item)))
|
||||
|
||||
def recursiveParser(item):
|
||||
if isinstance(item, list):
|
||||
packed = item[0]
|
||||
for i in item[1:]:
|
||||
packed += recursiveParser(i) + '|'
|
||||
return (packed[:-1] if packed[-1] == '|' else packed) + ']'
|
||||
type = item['type']
|
||||
index = item['index']
|
||||
if type == 'strings':
|
||||
return _base10To36(index)
|
||||
if type == 'integers':
|
||||
return _base10To36(stringLength + index)
|
||||
if type == 'floats':
|
||||
return _base10To36(stringLength + integerLength + index)
|
||||
if type == 'boolean':
|
||||
return str(index)
|
||||
if type == 'null':
|
||||
return str(TOKEN_NULL)
|
||||
if type == '':
|
||||
return str(TOKEN_UNDEFINED)
|
||||
if type == 'empty':
|
||||
return str(TOKEN_EMPTY_STRING)
|
||||
raise TypeError('The item is alien!')
|
||||
|
||||
ast = recursiveAstBuilder(json)
|
||||
|
||||
stringLength = len(dictionary['strings'])
|
||||
integerLength = len(dictionary['integers'])
|
||||
floatLength = len(dictionary['floats'])
|
||||
packed = '|'.join(dictionary['strings'])
|
||||
packed += '^' + '|'.join(dictionary['integers'])
|
||||
dictionary['floats'] = [str(n) for n in dictionary['floats']]
|
||||
packed += '^' + '|'.join(dictionary['floats'])
|
||||
packed += '^' + recursiveParser(ast)
|
||||
|
||||
return packed
|
||||
|
||||
def unpack(packed):
|
||||
rawBuffers = packed.split('^')
|
||||
dictionary = []
|
||||
buffer = rawBuffers[0]
|
||||
if buffer != '':
|
||||
buffer = buffer.split('|')
|
||||
for i in buffer:
|
||||
dictionary.append(_decode(i))
|
||||
buffer = rawBuffers[1]
|
||||
if buffer != '':
|
||||
buffer = buffer.split('|')
|
||||
for i in buffer:
|
||||
dictionary.append(_base36To10(i))
|
||||
buffer = rawBuffers[2]
|
||||
if buffer != '':
|
||||
buffer = buffer.split('|')
|
||||
for i in buffer:
|
||||
dictionary.append(float(i))
|
||||
tokens = []
|
||||
number36 = ''
|
||||
for i in rawBuffers[3]:
|
||||
if i in ['|', '$', '@', ']']:
|
||||
if number36:
|
||||
tokens.append(_base36To10(number36))
|
||||
number36 = ''
|
||||
if i != '|':
|
||||
tokens.append(i)
|
||||
else:
|
||||
number36 += i
|
||||
tokensLength = len(tokens)
|
||||
tokensIndex = 0
|
||||
|
||||
def recursiveUnpackerParser():
|
||||
nonlocal tokensIndex
|
||||
# Maybe '$' (object) or '@' (array)
|
||||
type = tokens[tokensIndex]
|
||||
tokensIndex += 1
|
||||
# Parse an array
|
||||
if type == '@':
|
||||
node = []
|
||||
while tokensIndex < tokensLength:
|
||||
value = tokens[tokensIndex]
|
||||
if value == ']':
|
||||
return node
|
||||
if value == '@' or value == '$':
|
||||
node.append(recursiveUnpackerParser())
|
||||
else:
|
||||
if value == TOKEN_TRUE:
|
||||
node.append(True)
|
||||
elif value == TOKEN_FALSE:
|
||||
node.append(False)
|
||||
elif value == TOKEN_NULL:
|
||||
node.append(None)
|
||||
elif value == TOKEN_UNDEFINED:
|
||||
node.append()
|
||||
elif value == TOKEN_EMPTY_STRING:
|
||||
node.append('')
|
||||
else:
|
||||
node.append(dictionary[value])
|
||||
tokensIndex += 1
|
||||
return node
|
||||
# Parse an object
|
||||
if type == '$':
|
||||
node = {}
|
||||
while tokensIndex < tokensLength:
|
||||
key = tokens[tokensIndex]
|
||||
if key == ']':
|
||||
return node
|
||||
if key == TOKEN_EMPTY_STRING:
|
||||
key = ''
|
||||
else:
|
||||
key = dictionary[key]
|
||||
tokensIndex += 1
|
||||
value = tokens[tokensIndex]
|
||||
if value == '@' or value == '$':
|
||||
node[key] = recursiveUnpackerParser()
|
||||
else:
|
||||
if value == TOKEN_TRUE:
|
||||
node[key] = True
|
||||
elif value == TOKEN_FALSE:
|
||||
node[key] = False
|
||||
elif value == TOKEN_NULL:
|
||||
node[key] = None
|
||||
elif value == TOKEN_UNDEFINED:
|
||||
node[key] = None
|
||||
elif value == TOKEN_EMPTY_STRING:
|
||||
node[key] = ''
|
||||
else:
|
||||
node[key] = dictionary[value]
|
||||
tokensIndex += 1
|
||||
return node
|
||||
raise TypeError('Bad token ' + str(type) + ' isn\'t a type')
|
||||
|
||||
return recursiveUnpackerParser()
|
||||
|
||||
def _indexOfDictionary(dictionary, value):
|
||||
if isinstance(value, bool):
|
||||
return TOKEN_TRUE if value else TOKEN_FALSE
|
||||
if value is None:
|
||||
return TOKEN_NULL
|
||||
if value == '':
|
||||
return TOKEN_UNDEFINED
|
||||
if value == '':
|
||||
return TOKEN_EMPTY_STRING
|
||||
if isinstance(value, str):
|
||||
value = _encode(value)
|
||||
index = dictionary['strings'].index(value) if value in dictionary['strings'] else -1
|
||||
if index == -1:
|
||||
dictionary['strings'].append(value)
|
||||
index = len(dictionary['strings']) - 1
|
||||
if type(value) not in [str, int]:
|
||||
raise Error('The type is not a JSON type')
|
||||
if isinstance(value, str):
|
||||
value = _encode(value)
|
||||
elif isinstance(value, int) and value % 1 == 0:
|
||||
value = _base10To36(value)
|
||||
value = _encode(value) if isinstance(value, str) else _base10To36(value)
|
||||
index = dictionary[type(value)].index(value) if value in dictionary[type(value)] else -1
|
||||
if index == -1:
|
||||
dictionary[type(value)].append(value)
|
||||
index = len(dictionary[type(value)]) - 1
|
||||
return '+' + str(index) if type(value) == 'number' else index
|
||||
|
||||
def _encode(string):
|
||||
if not isinstance(string, str):
|
||||
return string
|
||||
return string.replace(' ', '+').replace('+', '%2B').replace('|', '%7C').replace('^', '%5E').replace('%', '%25')
|
||||
|
||||
def _decode(string):
|
||||
if not isinstance(string, str):
|
||||
return string
|
||||
return string.replace('+', ' ').replace('%2B', '+').replace('%7C', '|').replace('%5E', '^').replace('%25', '%')
|
||||
|
||||
def _base10To36(number):
|
||||
if not isinstance(number, (int, float)):
|
||||
raise TypeError('number must be an integer')
|
||||
is_negative = number < 0
|
||||
number = abs(number)
|
||||
|
||||
alphabet, base36 = ['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', '']
|
||||
|
||||
while number:
|
||||
number, i = divmod(number, 36)
|
||||
base36 = alphabet[i] + base36
|
||||
if is_negative:
|
||||
base36 = '-' + base36
|
||||
|
||||
return base36 or alphabet[0]
|
||||
|
||||
|
||||
def _base36To10(number):
|
||||
return int(number, 36)
|
||||
|
||||
def _indexOf(array, obj, start=0):
|
||||
for i in range(start, len(array)):
|
||||
if array[i] == obj:
|
||||
return i
|
||||
return -1
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import json
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
import src.web as web
|
||||
import src.utils as utils
|
||||
|
||||
class module():
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.module = None
|
||||
self.rootdir = None
|
||||
self.tabs = []
|
||||
|
||||
def initSelf(self):
|
||||
spec = importlib.util.spec_from_file_location(self.name, utils.getRoot(self.entrypoint))
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
self.module = module
|
||||
|
||||
def run(self, moduleMaster):
|
||||
self.module.main(moduleMaster)
|
||||
|
||||
class moduleMaster():
|
||||
def __init__(self):
|
||||
self.modules = []
|
||||
self.webserv = None
|
||||
|
||||
self.app = None
|
||||
self.rawServer = None
|
||||
self.authServer = None
|
||||
|
||||
def initModules(self, webserv):
|
||||
self.webserv = webserv
|
||||
|
||||
mdirs = utils.listSubdirs(utils.getRoot('modules/'))
|
||||
|
||||
for mname in mdirs:
|
||||
mjson = json.loads(open(utils.getRoot(f'modules/{mname}/module.json')).read())
|
||||
m = module()
|
||||
m.name = mjson['name']
|
||||
m.entrypoint = mjson['entrypoint']
|
||||
|
||||
for tab in mjson['tabs']:
|
||||
mtab = utils.getatribinarr(self.webserv.webtabs, 'name', tab['name'])
|
||||
|
||||
if mtab == None:
|
||||
mtab = web.webtab()
|
||||
mtab.name = tab['name']
|
||||
m.tabs.append(mtab)
|
||||
|
||||
def recursiveAdder(objs):
|
||||
tmpPages = []
|
||||
for obj in objs:
|
||||
if obj['type'] == 'folder':
|
||||
folder = web.webpagefolder()
|
||||
folder.name = obj['name']
|
||||
tmpTmpPages = recursiveAdder(obj['pages'])
|
||||
for tmpobj in tmpTmpPages:
|
||||
folder.pages.append(tmpobj)
|
||||
tmpPages.append(folder)
|
||||
else:
|
||||
mpage = web.webpage()
|
||||
mpage.name = obj['name']
|
||||
mpage.location = obj['location']
|
||||
tmpPages.append(mpage)
|
||||
return tmpPages
|
||||
|
||||
tmpPages = recursiveAdder(tab['pages'])
|
||||
|
||||
for obj in tmpPages:
|
||||
mtab.pages.append(obj)
|
||||
|
||||
mtab.defaultPage = tab['defaultPage']
|
||||
|
||||
self.webserv.webtabs.append(mtab)
|
||||
|
||||
m.initSelf()
|
||||
self.modules.append(m)
|
||||
|
||||
for tab in webserv.webtabs:
|
||||
tab.addHtml()
|
||||
|
||||
def runModules(self):
|
||||
self.app = self.webserv.app
|
||||
self.rawServer = self.app.rawServer
|
||||
self.authServer = self.app.authServer
|
||||
|
||||
for module in self.modules:
|
||||
module.run(self)
|
||||
|
||||
def getRawClients(self):
|
||||
return self.rawServer.clients
|
||||
|
||||
def addRawEventListener(self, eventName, func):
|
||||
self.rawServer.addEventListener(eventName, func)
|
||||
|
||||
def getAuthClients(self):
|
||||
return self.authServer.clients
|
||||
|
||||
def addAuthEventListener(self, eventName, func):
|
||||
def tmpfunc(self, c, data):
|
||||
if not c in self.rawServer.clients:
|
||||
return
|
||||
ac = utils.getatribinarr(self.authServer.clients, 'rawClient', c)
|
||||
if ac == None:
|
||||
return
|
||||
|
||||
func(ac, data)
|
||||
|
||||
self.rawServer.addEventListener(eventName, tmpfunc)
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
import os
|
||||
import base64
|
||||
from hashlib import sha256
|
||||
from flask import Flask, render_template, Response
|
||||
from flask import request, redirect, url_for, make_response
|
||||
|
||||
import src.jsonpack as jsonpack
|
||||
import src.utils as utils
|
||||
|
||||
import queue
|
||||
|
||||
class rawClient:
|
||||
def __init__(self, app):
|
||||
self.clientid = utils.randID(32)
|
||||
self.messages = queue.Queue()
|
||||
self.app = app
|
||||
self.address = None
|
||||
def send(self, type, msg):
|
||||
self.app.rawServer.sendClient(self, jsonpack.pack({
|
||||
'type': type,
|
||||
'data': msg,
|
||||
'cid': self.clientid
|
||||
}))
|
||||
|
||||
#Credit to https://github.com/MaxHalford/flask-sse-no-deps
|
||||
class rawServer:
|
||||
|
||||
def __init__(self, app):
|
||||
self.eventListeners = []
|
||||
self.clients = []
|
||||
self.app = app
|
||||
app.rawServer = self.app
|
||||
|
||||
def listen(self):
|
||||
c = rawClient(self.app)
|
||||
self.clients.append(c)
|
||||
return self.clients[-1]
|
||||
|
||||
def broadcast(self, msg):
|
||||
# We go in reverse order because we might have to delete an element, which will shift the
|
||||
# indices backward
|
||||
ssedata = format_sse(msg)
|
||||
for i in reversed(range(len(clients))):
|
||||
try:
|
||||
self.clients[i].messages.put_nowait(ssedata)
|
||||
except queue.Full:
|
||||
del self.clients[i]
|
||||
|
||||
def sendClient(self, c, msg):
|
||||
if c not in self.clients:
|
||||
return
|
||||
ssedata = format_sse(msg)
|
||||
c.messages.put_nowait(ssedata)
|
||||
|
||||
def clientByCID(self, cid):
|
||||
for c in self.clients:
|
||||
if c.clientid == cid:
|
||||
return c
|
||||
return None
|
||||
|
||||
def addEventListener(self, eventName, func):
|
||||
self.eventListeners.append({
|
||||
'name': eventName,
|
||||
'func': func
|
||||
})
|
||||
|
||||
|
||||
def format_sse(data: str, event=None) -> str:
|
||||
#Formats a string and an event name in order to follow the event stream convention.
|
||||
msg = f'data: {data}\n\n'
|
||||
if event is not None:
|
||||
msg = f'event: {event}\n{msg}'
|
||||
return msg
|
||||
|
||||
|
||||
def startRawListener(app):
|
||||
app.rawServer = rawServer(app)
|
||||
|
||||
@app.route('/listen', methods=['GET', 'POST'])
|
||||
def listen():
|
||||
|
||||
if request.method == 'GET':
|
||||
|
||||
c = app.rawServer.listen() # returns a queue.Queue
|
||||
c.address = request.remote_addr
|
||||
c.send('clidata', {
|
||||
'cid': c.clientid
|
||||
})
|
||||
|
||||
def stream():
|
||||
while True:
|
||||
msg = c.messages.get() # blocks until a new message arrives
|
||||
yield msg
|
||||
|
||||
return Response(stream(), mimetype='text/event-stream')
|
||||
if request.method == 'POST':
|
||||
|
||||
data = jsonpack.unpack(request.data.decode("utf-8"))
|
||||
|
||||
if data['cid'] == None:
|
||||
return {}, 400
|
||||
c = utils.getatribinarr(app.rawServer.clients, 'clientid', data['cid'])
|
||||
if c == None:
|
||||
return {}, 400
|
||||
|
||||
for event in app.rawServer.eventListeners:
|
||||
if event['name'] == data['type']:
|
||||
event['func'](c, data)
|
||||
|
||||
return {}, 200
|
||||
|
||||
#return rawServer
|
||||
Executable
+124
@@ -0,0 +1,124 @@
|
||||
import os
|
||||
import json
|
||||
import string
|
||||
import random
|
||||
from hashlib import sha256
|
||||
from datetime import datetime
|
||||
|
||||
from sys import platform
|
||||
|
||||
import subprocess
|
||||
|
||||
def getRoot(path):
|
||||
rootdir = os.getcwd() + f'/{path}'
|
||||
|
||||
if platform in ['nt', 'win32', 'win64']:
|
||||
rootdir = rootdir.split(':')[1].replace('\\', '/')
|
||||
|
||||
return rootdir
|
||||
|
||||
def pathExists(path):
|
||||
return os.path.exists(path)
|
||||
|
||||
def makeDir(path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
def listSubdirs(folder):
|
||||
if os.path.exists(folder):
|
||||
return os.listdir(folder)
|
||||
else:
|
||||
return []
|
||||
|
||||
def writeFile(path, data):
|
||||
if not os.path.exists(path):
|
||||
with open(path, mode='a'): pass
|
||||
with open(path, 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
def readFile(path):
|
||||
if not os.path.exists(path):
|
||||
return ''
|
||||
try:
|
||||
with open(path) as f:
|
||||
return f.read()
|
||||
except:
|
||||
return ''
|
||||
|
||||
def delFile(path):
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
def getUnixTime():
|
||||
return round((datetime.utcnow() - datetime(1970, 1, 1)).total_seconds() * 1000)
|
||||
|
||||
def hash(data):
|
||||
return sha256(data.encode('utf-8')).hexdigest().upper()
|
||||
|
||||
def getatribinarr(arr, atribname, atrib):
|
||||
for i in range(0,len(arr),1):
|
||||
if getattr(arr[i], atribname) == atrib:
|
||||
return arr[i]
|
||||
return None
|
||||
|
||||
def randID(length):
|
||||
letters = string.hexdigits
|
||||
return ''.join(random.choice(letters) for i in range(length))
|
||||
|
||||
def genDefaultAccounts():
|
||||
userPassword = randID(16)
|
||||
adminPassword = randID(16)
|
||||
|
||||
print('#################################################')
|
||||
print('New Credentials - THESE ONLY WILL BE PRINTED ONCE')
|
||||
print('########')
|
||||
print('Username: User')
|
||||
print(f'Password: {userPassword}')
|
||||
print('########')
|
||||
print('Username: Admin')
|
||||
print(f'Password: {adminPassword}')
|
||||
print('#################################################')
|
||||
|
||||
return json.dumps(
|
||||
[
|
||||
{
|
||||
'username': 'User',
|
||||
'permGroups': [
|
||||
'Users'
|
||||
],
|
||||
'sha256passwordhash': hash(userPassword)
|
||||
},
|
||||
{
|
||||
'username': 'Admin',
|
||||
'permGroups': [
|
||||
'Users',
|
||||
'Admins'
|
||||
],
|
||||
'sha256passwordhash': hash(adminPassword)
|
||||
}
|
||||
], sort_keys=True, indent=2)
|
||||
|
||||
def genKey(path):
|
||||
subprocess.run(['openssl', 'genrsa', '-out', f'{path}selfsign.key', '2048'])
|
||||
|
||||
def genCert(path):
|
||||
writeFile(f'{path}/selfsign.cnf',
|
||||
"""[req]
|
||||
default_bits = 2048
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
encrypt_key = no
|
||||
distinguished_name = dn
|
||||
|
||||
[dn]
|
||||
C = ID
|
||||
O = Local Digital Cert Authority
|
||||
OU = www.ca.local
|
||||
CN = Self-Signed Root CA
|
||||
""")
|
||||
|
||||
subprocess.run(['openssl', 'req', '-new', '-key', f'{path}selfsign.key', '-out', f'{path}selfsign.csr', '-config', f'{path}selfsign.cnf'])
|
||||
subprocess.run(['openssl', 'x509', '-req', '-days', '36500', '-in', f'{path}selfsign.csr', '-signkey', f'{path}selfsign.key', '-out', f'{path}selfsign.crt'])
|
||||
|
||||
delFile(f'{path}selfsign.cnf')
|
||||
delFile(f'{path}selfsign.csr')
|
||||
Executable
+172
@@ -0,0 +1,172 @@
|
||||
import os
|
||||
import src.utils as utils
|
||||
import multiprocessing as mupr
|
||||
|
||||
from flask import Flask, render_template, Response
|
||||
from flask import request, redirect, url_for, make_response
|
||||
|
||||
import src.jsonpack as jsonpack
|
||||
import src.auth as auth
|
||||
|
||||
webroot = utils.getRoot('html/')
|
||||
|
||||
app = Flask(__name__,
|
||||
static_url_path=webroot,
|
||||
static_folder=webroot,
|
||||
template_folder=webroot)
|
||||
|
||||
class webtab():
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.pages = []
|
||||
self.defaultPage = ''
|
||||
self.html = ''
|
||||
|
||||
def recursiveAdder(self, objs):
|
||||
html = ''
|
||||
for obj in objs:
|
||||
if isinstance(obj, webpagefolder):
|
||||
html += '<details><summary>' +\
|
||||
obj.name +\
|
||||
'</summary><ul>\n' +\
|
||||
self.recursiveAdder(obj.pages) +\
|
||||
'</ul></details>\n'
|
||||
else:
|
||||
html += f'<li onclick=\'window.location="/{self.name}/{obj.name}"\'>' +\
|
||||
obj.name +\
|
||||
'</li>\n'
|
||||
return html
|
||||
|
||||
def addHtml(self):
|
||||
self.html = self.recursiveAdder(self.pages)
|
||||
|
||||
def addPage(self, page):
|
||||
self.pages.append(page)
|
||||
|
||||
class webpagefolder():
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.pages = []
|
||||
|
||||
class webpage():
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.location = None
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
isValid = app.authServer.cookieLogin(request)
|
||||
if not isValid:
|
||||
return redirect("/login", code=302)
|
||||
else:
|
||||
return redirect(f'/{app.defaultTab}/{app.defaultPage}', code=302)
|
||||
|
||||
@app.route('/login')
|
||||
def loginPage():
|
||||
isValid = app.authServer.cookieLogin(request)
|
||||
if isValid:
|
||||
return redirect(f'/{app.defaultTab}/{app.defaultPage}', code=302)
|
||||
|
||||
return make_response(open(f'{webroot}nav.html', 'r').read()
|
||||
.replace('<!--Place body here!!!-->', open(f'{webroot}login.html', 'r').read())
|
||||
.replace('<!--Place tabs here!!!-->', '<a href="/login" role="button" class="outline topnav-button text-white">Login</a>')
|
||||
.replace('<!--Place title here!!!-->', app.title)
|
||||
.replace('<!--Place defaultPage here!!!-->', '/login'))
|
||||
|
||||
def recursivePageLocationFinder(pagename, objs):
|
||||
returnVal = None
|
||||
for obj in objs:
|
||||
if isinstance(obj, webpagefolder):
|
||||
tmp = recursivePageLocationFinder(pagename, obj.pages)
|
||||
if tmp != None:
|
||||
returnVal = tmp
|
||||
else:
|
||||
if obj.name == pagename:
|
||||
returnVal = obj.location
|
||||
return returnVal
|
||||
|
||||
@app.route('/<tabname>/<pagename>')
|
||||
def page(tabname, pagename):
|
||||
|
||||
isValid = app.authServer.cookieLogin(request)
|
||||
if not isValid:
|
||||
return redirect("/login", code=302)
|
||||
|
||||
try:
|
||||
|
||||
tab = utils.getatribinarr(app.webtabs, 'name', tabname)
|
||||
pageloc = recursivePageLocationFinder(pagename, tab.pages)
|
||||
|
||||
return make_response(open(utils.getRoot('html/nav.html'), 'r').read()
|
||||
.replace('<!--Place body here!!!-->', open(utils.getRoot(pageloc), 'r').read())
|
||||
.replace('<!--Place tabs here!!!-->', app.tabHtml)
|
||||
.replace('<!--Place pages here!!!-->', tab.html)
|
||||
.replace('<!--Place title here!!!-->', app.title)
|
||||
.replace('<!--Place defaultPage here!!!-->', f'/{app.defaultTab}/{app.defaultPage}'))
|
||||
except:
|
||||
return redirect("/login", code=302)
|
||||
|
||||
@app.route('/src/<file>')
|
||||
def src(file):
|
||||
return app.send_static_file(f'src/{file}')
|
||||
|
||||
@app.errorhandler(404)
|
||||
def err404(err):
|
||||
return redirect("/", code=302)
|
||||
|
||||
class webserv():
|
||||
def __init__(self):
|
||||
self.title = 'Modulator'
|
||||
self.port = 443
|
||||
self.host = '0.0.0.0'
|
||||
self.verbose = False
|
||||
self.secure = True
|
||||
self.tabHtml = ''
|
||||
self.webtabs = []
|
||||
self.defaultTab = 'main'
|
||||
self.defaultPage = ''
|
||||
|
||||
self.app = None
|
||||
|
||||
def start(self):
|
||||
if not self.verbose:
|
||||
import logging
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.setLevel(logging.ERROR)
|
||||
|
||||
if self.secure:
|
||||
dataroot = utils.getRoot("data/")
|
||||
sslcontext = (f'{dataroot}selfsign.crt', f'{dataroot}selfsign.key')
|
||||
else:
|
||||
sslcontext = None
|
||||
|
||||
self.proc = mupr.Process(target=app.run, kwargs=dict(debug=self.verbose, port=self.port, host=self.host, ssl_context=sslcontext))
|
||||
|
||||
def tabHtml(path, name):
|
||||
return f'<a href="{path}" role="button" class="outline topnav-button text-white">{name}</a>'
|
||||
|
||||
for tab in self.webtabs:
|
||||
self.tabHtml += tabHtml(f'/{tab.name}/{tab.defaultPage}', tab.name)
|
||||
if tab.name == self.defaultTab:
|
||||
self.defaultPage = tab.defaultPage
|
||||
|
||||
app.authServer = auth.startAuthListener(app)
|
||||
|
||||
app.defaultTab = self.defaultTab
|
||||
app.defaultPage = self.defaultPage
|
||||
app.webtabs = self.webtabs
|
||||
app.tabHtml = self.tabHtml
|
||||
|
||||
app.title = self.title
|
||||
self.app = app
|
||||
self.proc.start()
|
||||
|
||||
def stop(self):
|
||||
self.proc.terminate()
|
||||
|
||||
# def sendfatal(self, err):
|
||||
# self.rawServer.broadcast(jsonpack.pack({
|
||||
# 'type': 'error',
|
||||
# 'severity': 'fatal',
|
||||
# 'error': err
|
||||
# }))
|
||||
Reference in New Issue
Block a user