aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2020-03-02 03:26:01 +0300
committerRblSb <msrblsb@gmail.com>2020-03-02 03:37:30 +0300
commitfe6875904f0c8f4ed8359efe961570307d9cbae6 (patch)
treec19c6274609c88b519ab5676ace5d7665512e153
parent2ade04273717807096a07b6eb132b7677917392d (diff)
addAdmin server command
-rw-r--r--.gitignore1
-rw-r--r--.vscode/launch.json3
-rw-r--r--build/server.js346
-rw-r--r--res/client.js286
-rw-r--r--res/index.html3
-rw-r--r--src/Types.hx15
-rw-r--r--src/client/Buttons.hx13
-rw-r--r--src/client/Main.hx46
-rw-r--r--src/server/ConsoleInput.hx59
-rw-r--r--src/server/Main.hx78
-rw-r--r--user/README.md11
11 files changed, 778 insertions, 83 deletions
diff --git a/.gitignore b/.gitignore
index 575465a..40a4754 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
/res/temp
/user/config.json
/user/state.json
+/user/users.json
/user/res/
/user/logs/
/user/crashes/
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 2153bb9..a511189 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -6,7 +6,8 @@
"name": "Node: build and run",
"program": "${workspaceFolder}/build/server.js",
"sourceMaps": true,
- "preLaunchTask": "Build all"
+ "preLaunchTask": "Build all",
+ "console": "integratedTerminal"
}
]
}
diff --git a/build/server.js b/build/server.js
index 0a90ca2..dac26c4 100644
--- a/build/server.js
+++ b/build/server.js
@@ -309,6 +309,20 @@ StringTools.startsWith = function(s,start) {
StringTools.replace = function(s,sub,by) {
return s.split(sub).join(by);
};
+StringTools.hex = function(n,digits) {
+ var s = "";
+ while(true) {
+ s = "0123456789ABCDEF".charAt(n & 15) + s;
+ n >>>= 4;
+ if(!(n > 0)) {
+ break;
+ }
+ }
+ if(digits != null) {
+ while(s.length < digits) s = "0" + s;
+ }
+ return s;
+};
var _$VideoList_VideoList_$Impl_$ = {};
_$VideoList_VideoList_$Impl_$.__name__ = true;
_$VideoList_VideoList_$Impl_$._new = function() {
@@ -409,6 +423,190 @@ haxe_Timer.prototype = {
,run: function() {
}
};
+var haxe_crypto_Sha256 = function() {
+};
+haxe_crypto_Sha256.__name__ = true;
+haxe_crypto_Sha256.encode = function(s) {
+ var sh = new haxe_crypto_Sha256();
+ return sh.hex(sh.doEncode(haxe_crypto_Sha256.str2blks(s),s.length * 8));
+};
+haxe_crypto_Sha256.str2blks = function(s) {
+ var s1 = haxe_io_Bytes.ofString(s);
+ var nblk = (s1.length + 8 >> 6) + 1;
+ var blks = [];
+ var _g = 0;
+ var _g1 = nblk * 16;
+ while(_g < _g1) blks[_g++] = 0;
+ var _g2 = 0;
+ var _g3 = s1.length;
+ while(_g2 < _g3) {
+ var i = _g2++;
+ blks[i >> 2] |= s1.b[i] << 24 - ((i & 3) << 3);
+ }
+ var i1 = s1.length;
+ blks[i1 >> 2] |= 128 << 24 - ((i1 & 3) << 3);
+ blks[nblk * 16 - 1] = s1.length * 8;
+ return blks;
+};
+haxe_crypto_Sha256.prototype = {
+ doEncode: function(m,l) {
+ var K = [1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998];
+ var HASH = [1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225];
+ var W = [];
+ W[64] = 0;
+ var a;
+ var b;
+ var c;
+ var d;
+ var e;
+ var f;
+ var g;
+ var h;
+ var T1;
+ var T2;
+ m[l >> 5] |= 128 << 24 - l % 32;
+ m[(l + 64 >> 9 << 4) + 15] = l;
+ var i = 0;
+ while(i < m.length) {
+ a = HASH[0];
+ b = HASH[1];
+ c = HASH[2];
+ d = HASH[3];
+ e = HASH[4];
+ f = HASH[5];
+ g = HASH[6];
+ h = HASH[7];
+ var _g = 0;
+ while(_g < 64) {
+ var j = _g++;
+ if(j < 16) {
+ W[j] = m[j + i];
+ } else {
+ var x = W[j - 2];
+ var x1 = (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ x >>> 10;
+ var y = W[j - 7];
+ var lsw = (x1 & 65535) + (y & 65535);
+ var x2 = (x1 >> 16) + (y >> 16) + (lsw >> 16) << 16 | lsw & 65535;
+ var x3 = W[j - 15];
+ var y1 = (x3 >>> 7 | x3 << 25) ^ (x3 >>> 18 | x3 << 14) ^ x3 >>> 3;
+ var lsw1 = (x2 & 65535) + (y1 & 65535);
+ var x4 = (x2 >> 16) + (y1 >> 16) + (lsw1 >> 16) << 16 | lsw1 & 65535;
+ var y2 = W[j - 16];
+ var lsw2 = (x4 & 65535) + (y2 & 65535);
+ W[j] = (x4 >> 16) + (y2 >> 16) + (lsw2 >> 16) << 16 | lsw2 & 65535;
+ }
+ var y3 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
+ var lsw3 = (h & 65535) + (y3 & 65535);
+ var x5 = (h >> 16) + (y3 >> 16) + (lsw3 >> 16) << 16 | lsw3 & 65535;
+ var y4 = e & f ^ ~e & g;
+ var lsw4 = (x5 & 65535) + (y4 & 65535);
+ var x6 = (x5 >> 16) + (y4 >> 16) + (lsw4 >> 16) << 16 | lsw4 & 65535;
+ var y5 = K[j];
+ var lsw5 = (x6 & 65535) + (y5 & 65535);
+ var x7 = (x6 >> 16) + (y5 >> 16) + (lsw5 >> 16) << 16 | lsw5 & 65535;
+ var y6 = W[j];
+ var lsw6 = (x7 & 65535) + (y6 & 65535);
+ T1 = (x7 >> 16) + (y6 >> 16) + (lsw6 >> 16) << 16 | lsw6 & 65535;
+ var x8 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
+ var y7 = a & b ^ a & c ^ b & c;
+ var lsw7 = (x8 & 65535) + (y7 & 65535);
+ T2 = (x8 >> 16) + (y7 >> 16) + (lsw7 >> 16) << 16 | lsw7 & 65535;
+ h = g;
+ g = f;
+ f = e;
+ var lsw8 = (d & 65535) + (T1 & 65535);
+ e = (d >> 16) + (T1 >> 16) + (lsw8 >> 16) << 16 | lsw8 & 65535;
+ d = c;
+ c = b;
+ b = a;
+ var lsw9 = (T1 & 65535) + (T2 & 65535);
+ a = (T1 >> 16) + (T2 >> 16) + (lsw9 >> 16) << 16 | lsw9 & 65535;
+ }
+ var y8 = HASH[0];
+ var lsw10 = (a & 65535) + (y8 & 65535);
+ HASH[0] = (a >> 16) + (y8 >> 16) + (lsw10 >> 16) << 16 | lsw10 & 65535;
+ var y9 = HASH[1];
+ var lsw11 = (b & 65535) + (y9 & 65535);
+ HASH[1] = (b >> 16) + (y9 >> 16) + (lsw11 >> 16) << 16 | lsw11 & 65535;
+ var y10 = HASH[2];
+ var lsw12 = (c & 65535) + (y10 & 65535);
+ HASH[2] = (c >> 16) + (y10 >> 16) + (lsw12 >> 16) << 16 | lsw12 & 65535;
+ var y11 = HASH[3];
+ var lsw13 = (d & 65535) + (y11 & 65535);
+ HASH[3] = (d >> 16) + (y11 >> 16) + (lsw13 >> 16) << 16 | lsw13 & 65535;
+ var y12 = HASH[4];
+ var lsw14 = (e & 65535) + (y12 & 65535);
+ HASH[4] = (e >> 16) + (y12 >> 16) + (lsw14 >> 16) << 16 | lsw14 & 65535;
+ var y13 = HASH[5];
+ var lsw15 = (f & 65535) + (y13 & 65535);
+ HASH[5] = (f >> 16) + (y13 >> 16) + (lsw15 >> 16) << 16 | lsw15 & 65535;
+ var y14 = HASH[6];
+ var lsw16 = (g & 65535) + (y14 & 65535);
+ HASH[6] = (g >> 16) + (y14 >> 16) + (lsw16 >> 16) << 16 | lsw16 & 65535;
+ var y15 = HASH[7];
+ var lsw17 = (h & 65535) + (y15 & 65535);
+ HASH[7] = (h >> 16) + (y15 >> 16) + (lsw17 >> 16) << 16 | lsw17 & 65535;
+ i += 16;
+ }
+ return HASH;
+ }
+ ,hex: function(a) {
+ var str = "";
+ var _g = 0;
+ while(_g < a.length) str += StringTools.hex(a[_g++],8);
+ return str.toLowerCase();
+ }
+};
+var haxe_io_Bytes = function(data) {
+ this.length = data.byteLength;
+ this.b = new Uint8Array(data);
+ this.b.bufferValue = data;
+ data.hxBytes = this;
+ data.bytes = this.b;
+};
+haxe_io_Bytes.__name__ = true;
+haxe_io_Bytes.ofString = function(s,encoding) {
+ if(encoding == haxe_io_Encoding.RawNative) {
+ var buf = new Uint8Array(s.length << 1);
+ var _g = 0;
+ var _g1 = s.length;
+ while(_g < _g1) {
+ var i = _g++;
+ var c = s.charCodeAt(i);
+ buf[i << 1] = c & 255;
+ buf[i << 1 | 1] = c >> 8;
+ }
+ return new haxe_io_Bytes(buf.buffer);
+ }
+ var a = [];
+ var i1 = 0;
+ while(i1 < s.length) {
+ var c1 = s.charCodeAt(i1++);
+ if(55296 <= c1 && c1 <= 56319) {
+ c1 = c1 - 55232 << 10 | s.charCodeAt(i1++) & 1023;
+ }
+ if(c1 <= 127) {
+ a.push(c1);
+ } else if(c1 <= 2047) {
+ a.push(192 | c1 >> 6);
+ a.push(128 | c1 & 63);
+ } else if(c1 <= 65535) {
+ a.push(224 | c1 >> 12);
+ a.push(128 | c1 >> 6 & 63);
+ a.push(128 | c1 & 63);
+ } else {
+ a.push(240 | c1 >> 18);
+ a.push(128 | c1 >> 12 & 63);
+ a.push(128 | c1 >> 6 & 63);
+ a.push(128 | c1 & 63);
+ }
+ }
+ return new haxe_io_Bytes(new Uint8Array(a).buffer);
+};
+var haxe_io_Encoding = $hxEnums["haxe.io.Encoding"] = { __ename__ : true, __constructs__ : ["UTF8","RawNative"]
+ ,UTF8: {_hx_index:0,__enum__:"haxe.io.Encoding",toString:$estr}
+ ,RawNative: {_hx_index:1,__enum__:"haxe.io.Encoding",toString:$estr}
+};
var haxe_io_Path = function(path) {
switch(path) {
case ".":case "..":
@@ -565,7 +763,52 @@ var js_node_Fs = require("fs");
var js_node_Http = require("http");
var js_node_Os = require("os");
var js_node_Path = require("path");
+var js_node_Readline = require("readline");
var js_npm_ws_Server = require("ws").Server;
+var server_ConsoleInput = function(main) {
+ this.main = main;
+};
+server_ConsoleInput.__name__ = true;
+server_ConsoleInput.prototype = {
+ initConsoleInput: function() {
+ var _gthis = this;
+ var rl = js_node_Readline.createInterface(process.stdin,process.stdout);
+ haxe_Log.trace = function(msg,pos) {
+ js_node_Readline.clearLine(process.stdout,0);
+ js_node_Readline.cursorTo(process.stdout,0,null);
+ console.log(msg);
+ rl.prompt(true);
+ return;
+ };
+ rl.prompt();
+ rl.on("line",function(line) {
+ _gthis.parseLine(line);
+ rl.prompt();
+ return;
+ });
+ }
+ ,parseLine: function(line) {
+ if(StringTools.startsWith(line,"/addAdmin")) {
+ var args = line.split(" ");
+ if(args.length != 3) {
+ haxe_Log.trace("Wrong count of arguments",{ fileName : "src/server/ConsoleInput.hx", lineNumber : 36, className : "server.ConsoleInput", methodName : "parseLine"});
+ return;
+ }
+ var name = args[1];
+ var password = args[2];
+ if(this.main.badNickName(name)) {
+ haxe_Log.trace(StringTools.replace(Lang.get("usernameError"),"$MAX","" + this.main.config.maxLoginLength),{ fileName : "src/server/ConsoleInput.hx", lineNumber : 44, className : "server.ConsoleInput", methodName : "parseLine"});
+ return;
+ }
+ this.main.addAdmin(name,password);
+ } else if(line == "/exit") {
+ this.main.exit();
+ return;
+ } else {
+ haxe_Log.trace("Unknown command \"" + line + "\". List:\n/addAdmin name password | Adds channel admin\n/exit | Exit process",{ fileName : "src/server/ConsoleInput.hx", lineNumber : 53, className : "server.ConsoleInput", methodName : "parseLine"});
+ }
+ }
+};
var server_HttpServer = function() { };
server_HttpServer.__name__ = true;
server_HttpServer.init = function(dir,customDir) {
@@ -701,34 +944,34 @@ var server_Main = function(port,wsPort) {
port = envPort;
}
this.statePath = "" + this.rootDir + "/user/state.json";
- var exit = function() {
- _gthis.saveState();
- process.exit();
- };
- process.on("SIGINT",exit);
- process.on("SIGUSR1",exit);
- process.on("SIGUSR2",exit);
- process.on("SIGTERM",exit);
+ process.on("SIGINT",$bind(this,this.exit));
+ process.on("SIGUSR1",$bind(this,this.exit));
+ process.on("SIGUSR2",$bind(this,this.exit));
+ process.on("SIGTERM",$bind(this,this.exit));
process.on("uncaughtException",function(err) {
_gthis.logError("uncaughtException",{ message : err.message, stack : err.stack});
- exit();
+ _gthis.exit();
return;
});
process.on("unhandledRejection",function(reason,promise) {
_gthis.logError("unhandledRejection",reason);
- exit();
+ _gthis.exit();
return;
});
+ this.consoleInput = new server_ConsoleInput(this);
+ this.consoleInput.initConsoleInput();
this.initIntergationHandlers();
this.loadState();
- this.config = this.getUserConfig();
+ this.config = this.loadUserConfig();
+ this.userList = this.loadUsers();
+ this.config.salt = this.generateConfigSalt();
this.localIp = server_Utils.getLocalIp();
this.globalIp = this.localIp;
this.port = port;
server_Utils.getGlobalIp(function(ip) {
_gthis.globalIp = ip;
- haxe_Log.trace("Local: http://" + _gthis.localIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 71, className : "server.Main", methodName : "new"});
- haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 72, className : "server.Main", methodName : "new"});
+ haxe_Log.trace("Local: http://" + _gthis.localIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 74, className : "server.Main", methodName : "new"});
+ haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 75, className : "server.Main", methodName : "new"});
return;
});
var dir = "" + this.rootDir + "/res";
@@ -747,7 +990,18 @@ server_Main.main = function() {
new server_Main();
};
server_Main.prototype = {
- getUserConfig: function() {
+ exit: function() {
+ this.saveState();
+ process.exit();
+ }
+ ,generateConfigSalt: function() {
+ if(this.userList.salt == null) {
+ var tmp = "" + Math.random();
+ this.userList.salt = haxe_crypto_Sha256.encode(tmp);
+ }
+ return this.userList.salt;
+ }
+ ,loadUserConfig: function() {
var config = JSON.parse(js_node_Fs.readFileSync("" + this.rootDir + "/default-config.json",{ encoding : "utf8"}));
var customPath = "" + this.rootDir + "/user/config.json";
if(!sys_FileSystem.exists(customPath)) {
@@ -760,14 +1014,28 @@ server_Main.prototype = {
var field = _g1[_g];
++_g;
if(Reflect.field(config,field) == null) {
- haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 93, className : "server.Main", methodName : "getUserConfig"});
+ haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 107, className : "server.Main", methodName : "loadUserConfig"});
}
config[field] = Reflect.field(customConfig,field);
}
return config;
}
+ ,loadUsers: function() {
+ var customPath = "" + this.rootDir + "/user/users.json";
+ if(!sys_FileSystem.exists(customPath)) {
+ return { admins : []};
+ }
+ return JSON.parse(js_node_Fs.readFileSync(customPath,{ encoding : "utf8"}));
+ }
+ ,writeUsers: function(users) {
+ var folder = "" + this.rootDir + "/user";
+ if(!sys_FileSystem.exists(folder)) {
+ sys_FileSystem.createDirectory(folder);
+ }
+ js_node_Fs.writeFileSync("" + folder + "/users.json",JSON.stringify(users,null,"\t"));
+ }
,saveState: function() {
- haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 100, className : "server.Main", methodName : "saveState"});
+ haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 131, className : "server.Main", methodName : "saveState"});
var json = JSON.stringify({ videoList : this.videoList, itemPos : this.itemPos, messages : this.messages, timer : { time : this.videoTimer.getTime(), paused : this.videoTimer.isPaused()}},null,"\t");
js_node_Fs.writeFileSync(this.statePath,json);
}
@@ -775,7 +1043,7 @@ server_Main.prototype = {
if(!sys_FileSystem.exists(this.statePath)) {
return;
}
- haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 116, className : "server.Main", methodName : "loadState"});
+ haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 147, className : "server.Main", methodName : "loadState"});
var data = JSON.parse(js_node_Fs.readFileSync(this.statePath,{ encoding : "utf8"}));
this.videoList.length = 0;
this.messages.length = 0;
@@ -791,7 +1059,7 @@ server_Main.prototype = {
this.videoTimer.pause();
}
,logError: function(type,data) {
- haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 129, className : "server.Main", methodName : "logError", customParams : [data]});
+ haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 160, className : "server.Main", methodName : "logError", customParams : [data]});
var crashesFolder = "" + this.rootDir + "/user/crashes";
var name = new Date().toISOString() + "-" + type;
if(!sys_FileSystem.exists(crashesFolder)) {
@@ -807,23 +1075,31 @@ server_Main.prototype = {
return;
}
var url = "http://" + process.env["APP_URL"];
- haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 143, className : "server.Main", methodName : "initIntergationHandlers"});
+ haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 174, className : "server.Main", methodName : "initIntergationHandlers"});
js_node_Http.get(url,function(r) {
return;
});
};
}
}
+ ,addAdmin: function(name,password) {
+ password += this.config.salt;
+ var hash = haxe_crypto_Sha256.encode(password);
+ if(this.userList.admins == null) {
+ this.userList.admins = [];
+ }
+ this.userList.admins.push({ name : name, hash : hash});
+ this.writeUsers(this.userList);
+ haxe_Log.trace("Admin " + name + " added.",{ fileName : "src/server/Main.hx", lineNumber : 189, className : "server.Main", methodName : "addAdmin"});
+ }
,onConnect: function(ws,req) {
var _gthis = this;
var ip = req.connection.remoteAddress;
var id = this.freeIds.length > 0 ? this.freeIds.shift() : this.clients.length;
var name = "Guest " + (id + 1);
- haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 153, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 196, className : "server.Main", methodName : "onConnect"});
var client = new Client(ws,req,id,name,0);
- if(req.connection.localAddress == ip) {
- client.group |= 4;
- }
+ client.setGroupFlag(ClientGroup.Admin,req.connection.localAddress == ip);
this.clients.push(client);
if(this.clients.length == 1 && this.videoList.length > 0) {
if(this.videoTimer.isPaused()) {
@@ -845,7 +1121,7 @@ server_Main.prototype = {
return;
});
ws.on("close",function(err) {
- haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 182, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 225, className : "server.Main", methodName : "onConnect"});
server_Utils.sortedPush(_gthis.freeIds,client.id);
HxOverrides.remove(_gthis.clients,client);
_gthis.sendClientList();
@@ -914,6 +1190,26 @@ server_Main.prototype = {
this.send(client,{ type : "LoginError"});
return;
}
+ var hash = data.login.passHash;
+ if(hash == null) {
+ if(Lambda.exists(this.userList.admins,function(a) {
+ return a.name == name;
+ })) {
+ this.send(client,{ type : "PasswordRequest"});
+ return;
+ }
+ } else if(Lambda.exists(this.userList.admins,function(a1) {
+ if(a1.name == name) {
+ return a1.hash == hash;
+ } else {
+ return false;
+ }
+ })) {
+ client.setGroupFlag(ClientGroup.Admin,true);
+ } else {
+ this.send(client,{ type : "LoginError"});
+ return;
+ }
client.name = name;
client.setGroupFlag(ClientGroup.User,true);
this.send(client,{ type : data.type, login : { isUnknownClient : true, clientName : client.name, clients : this.clientList()}});
@@ -945,6 +1241,8 @@ server_Main.prototype = {
}
this.broadcast(data);
break;
+ case "PasswordRequest":
+ break;
case "Pause":
if(this.videoList.length == 0) {
return;
diff --git a/res/client.js b/res/client.js
index 7b90a05..14feb83 100644
--- a/res/client.js
+++ b/res/client.js
@@ -118,25 +118,6 @@ Lambda.exists = function(it,f) {
}
return false;
};
-var haxe_ds_StringMap = function() {
- this.h = { };
-};
-haxe_ds_StringMap.__name__ = true;
-haxe_ds_StringMap.prototype = {
- setReserved: function(key,value) {
- if(this.rh == null) {
- this.rh = { };
- }
- this.rh["$" + key] = value;
- }
- ,getReserved: function(key) {
- if(this.rh == null) {
- return null;
- } else {
- return this.rh["$" + key];
- }
- }
-};
var Lang = function() { };
Lang.__name__ = true;
Lang.request = function(path,callback) {
@@ -326,6 +307,20 @@ StringTools.startsWith = function(s,start) {
StringTools.replace = function(s,sub,by) {
return s.split(sub).join(by);
};
+StringTools.hex = function(n,digits) {
+ var s = "";
+ while(true) {
+ s = "0123456789ABCDEF".charAt(n & 15) + s;
+ n >>>= 4;
+ if(!(n > 0)) {
+ break;
+ }
+ }
+ if(digits != null) {
+ while(s.length < digits) s = "0" + s;
+ }
+ return s;
+};
var _$VideoList_VideoList_$Impl_$ = {};
_$VideoList_VideoList_$Impl_$.__name__ = true;
_$VideoList_VideoList_$Impl_$._new = function() {
@@ -588,17 +583,21 @@ client_Buttons.hideMenus = function() {
client_Buttons.initChatInput = function(main) {
var guestName = window.document.querySelector("#guestname");
guestName.onkeydown = function(e) {
- if(guestName.value.length == 0) {
- return;
- }
if(e.keyCode == 13) {
- main.send({ type : "Login", login : { clientName : guestName.value}});
+ main.guestLogin(guestName.value);
+ }
+ return;
+ };
+ var guestPass = window.document.querySelector("#guestpass");
+ guestPass.onkeydown = function(e1) {
+ if(e1.keyCode == 13) {
+ main.userLogin(guestName.value,guestPass.value);
}
return;
};
var chatLine = window.document.querySelector("#chatline");
- chatLine.onkeydown = function(e1) {
- switch(e1.keyCode) {
+ chatLine.onkeydown = function(e2) {
+ switch(e2.keyCode) {
case 13:
if(chatLine.value.length == 0) {
return;
@@ -821,7 +820,7 @@ client_Main.prototype = {
var data = JSON.parse(e.data);
var t = data.type;
var t1 = t.charAt(0).toLowerCase() + HxOverrides.substr(t,1,null);
- haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 211, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
+ haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 212, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
switch(data.type) {
case "AddVideo":
this.player.addVideoItem(data.addVideo.item,data.addVideo.atEnd);
@@ -868,6 +867,7 @@ client_Main.prototype = {
case "LoginError":
var text = StringTools.replace(Lang.get("usernameError"),"$MAX","" + this.config.maxLoginLength);
this.serverMessage(4,text);
+ this.showGuestLoginPanel();
break;
case "Logout":
this.updateClients(data.logout.clients);
@@ -877,6 +877,9 @@ client_Main.prototype = {
case "Message":
this.addMessage(data.message.clientName,data.message.text);
break;
+ case "PasswordRequest":
+ this.showGuestPasswordPanel();
+ break;
case "Pause":
if((this.personal.group & 1 << ClientGroup.Leader._hx_index) != 0) {
return;
@@ -958,8 +961,11 @@ client_Main.prototype = {
this.onLogin(connected.clients,connected.clientName);
}
var guestName = window.document.querySelector("#guestname");
- if(guestName.value.length > 0) {
- this.send({ type : "Login", login : { clientName : guestName.value}});
+ var guestPass = window.document.querySelector("#guestpass");
+ if(this.config.salt != null && guestPass.value.length > 0) {
+ this.userLogin(guestName.value,guestPass.value);
+ } else {
+ this.guestLogin(guestName.value);
}
this.clearChat();
this.serverMessage(1);
@@ -972,6 +978,24 @@ client_Main.prototype = {
}
this.player.setItems(connected.videoList,connected.itemPos);
}
+ ,guestLogin: function(name) {
+ if(name.length == 0) {
+ return;
+ }
+ this.send({ type : "Login", login : { clientName : name}});
+ }
+ ,userLogin: function(name,password) {
+ if(this.config.salt == null) {
+ return;
+ }
+ if(password.length == 0) {
+ return;
+ }
+ if(name.length == 0) {
+ return;
+ }
+ this.send({ type : "Login", login : { clientName : name, passHash : haxe_crypto_Sha256.encode(password + this.config.salt)}});
+ }
,setConfig: function(config) {
this.config = config;
this.pageTitle = config.channelName;
@@ -994,9 +1018,8 @@ client_Main.prototype = {
}
var smilesWrap = window.document.querySelector("#smileswrap");
smilesWrap.onclick = function(e) {
- var el = e.target;
var form = window.document.querySelector("#chatline");
- form.value += " " + el.title;
+ form.value += " " + e.target.title;
form.focus();
return;
};
@@ -1024,12 +1047,14 @@ client_Main.prototype = {
}
,showGuestLoginPanel: function() {
window.document.querySelector("#guestlogin").style.display = "block";
+ window.document.querySelector("#guestpassword").style.display = "none";
window.document.querySelector("#chatline").style.display = "none";
window.document.querySelector("#exitBtn").textContent = Lang.get("login");
window.dispatchEvent(new Event("resize"));
}
,hideGuestLoginPanel: function() {
window.document.querySelector("#guestlogin").style.display = "none";
+ window.document.querySelector("#guestpassword").style.display = "none";
window.document.querySelector("#chatline").style.display = "block";
window.document.querySelector("#exitBtn").textContent = Lang.get("exit");
if((this.personal.group & 4) != 0) {
@@ -1037,6 +1062,11 @@ client_Main.prototype = {
}
window.dispatchEvent(new Event("resize"));
}
+ ,showGuestPasswordPanel: function() {
+ window.document.querySelector("#guestlogin").style.display = "none";
+ window.document.querySelector("#chatline").style.display = "none";
+ window.document.querySelector("#guestpassword").style.display = "block";
+ }
,updateClients: function(newClients) {
this.clients.length = 0;
var _g = 0;
@@ -1719,7 +1749,8 @@ client_players_Youtube.prototype = {
if(_gthis.playerEl.contains(video)) {
_gthis.playerEl.removeChild(video);
}
- callback({ duration : _gthis.youtube.getDuration()});
+ var tmp = _gthis.youtube.getDuration();
+ callback({ duration : tmp});
return;
}, onError : function(e1) {
haxe_Log.trace("Error " + e1.data,{ fileName : "src/client/players/Youtube.hx", lineNumber : 116, className : "client.players.Youtube", methodName : "getRemoteDataFallback"});
@@ -1844,6 +1875,159 @@ haxe_Timer.prototype = {
,run: function() {
}
};
+var haxe_crypto_Sha256 = function() {
+};
+haxe_crypto_Sha256.__name__ = true;
+haxe_crypto_Sha256.encode = function(s) {
+ var sh = new haxe_crypto_Sha256();
+ return sh.hex(sh.doEncode(haxe_crypto_Sha256.str2blks(s),s.length * 8));
+};
+haxe_crypto_Sha256.str2blks = function(s) {
+ var s1 = haxe_io_Bytes.ofString(s);
+ var nblk = (s1.length + 8 >> 6) + 1;
+ var blks = [];
+ var _g = 0;
+ var _g1 = nblk * 16;
+ while(_g < _g1) blks[_g++] = 0;
+ var _g2 = 0;
+ var _g3 = s1.length;
+ while(_g2 < _g3) {
+ var i = _g2++;
+ blks[i >> 2] |= s1.b[i] << 24 - ((i & 3) << 3);
+ }
+ var i1 = s1.length;
+ blks[i1 >> 2] |= 128 << 24 - ((i1 & 3) << 3);
+ blks[nblk * 16 - 1] = s1.length * 8;
+ return blks;
+};
+haxe_crypto_Sha256.prototype = {
+ doEncode: function(m,l) {
+ var K = [1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998];
+ var HASH = [1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225];
+ var W = [];
+ W[64] = 0;
+ var a;
+ var b;
+ var c;
+ var d;
+ var e;
+ var f;
+ var g;
+ var h;
+ var T1;
+ var T2;
+ m[l >> 5] |= 128 << 24 - l % 32;
+ m[(l + 64 >> 9 << 4) + 15] = l;
+ var i = 0;
+ while(i < m.length) {
+ a = HASH[0];
+ b = HASH[1];
+ c = HASH[2];
+ d = HASH[3];
+ e = HASH[4];
+ f = HASH[5];
+ g = HASH[6];
+ h = HASH[7];
+ var _g = 0;
+ while(_g < 64) {
+ var j = _g++;
+ if(j < 16) {
+ W[j] = m[j + i];
+ } else {
+ var x = W[j - 2];
+ var x1 = (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ x >>> 10;
+ var y = W[j - 7];
+ var lsw = (x1 & 65535) + (y & 65535);
+ var x2 = (x1 >> 16) + (y >> 16) + (lsw >> 16) << 16 | lsw & 65535;
+ var x3 = W[j - 15];
+ var y1 = (x3 >>> 7 | x3 << 25) ^ (x3 >>> 18 | x3 << 14) ^ x3 >>> 3;
+ var lsw1 = (x2 & 65535) + (y1 & 65535);
+ var x4 = (x2 >> 16) + (y1 >> 16) + (lsw1 >> 16) << 16 | lsw1 & 65535;
+ var y2 = W[j - 16];
+ var lsw2 = (x4 & 65535) + (y2 & 65535);
+ W[j] = (x4 >> 16) + (y2 >> 16) + (lsw2 >> 16) << 16 | lsw2 & 65535;
+ }
+ var y3 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
+ var lsw3 = (h & 65535) + (y3 & 65535);
+ var x5 = (h >> 16) + (y3 >> 16) + (lsw3 >> 16) << 16 | lsw3 & 65535;
+ var y4 = e & f ^ ~e & g;
+ var lsw4 = (x5 & 65535) + (y4 & 65535);
+ var x6 = (x5 >> 16) + (y4 >> 16) + (lsw4 >> 16) << 16 | lsw4 & 65535;
+ var y5 = K[j];
+ var lsw5 = (x6 & 65535) + (y5 & 65535);
+ var x7 = (x6 >> 16) + (y5 >> 16) + (lsw5 >> 16) << 16 | lsw5 & 65535;
+ var y6 = W[j];
+ var lsw6 = (x7 & 65535) + (y6 & 65535);
+ T1 = (x7 >> 16) + (y6 >> 16) + (lsw6 >> 16) << 16 | lsw6 & 65535;
+ var x8 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
+ var y7 = a & b ^ a & c ^ b & c;
+ var lsw7 = (x8 & 65535) + (y7 & 65535);
+ T2 = (x8 >> 16) + (y7 >> 16) + (lsw7 >> 16) << 16 | lsw7 & 65535;
+ h = g;
+ g = f;
+ f = e;
+ var lsw8 = (d & 65535) + (T1 & 65535);
+ e = (d >> 16) + (T1 >> 16) + (lsw8 >> 16) << 16 | lsw8 & 65535;
+ d = c;
+ c = b;
+ b = a;
+ var lsw9 = (T1 & 65535) + (T2 & 65535);
+ a = (T1 >> 16) + (T2 >> 16) + (lsw9 >> 16) << 16 | lsw9 & 65535;
+ }
+ var y8 = HASH[0];
+ var lsw10 = (a & 65535) + (y8 & 65535);
+ HASH[0] = (a >> 16) + (y8 >> 16) + (lsw10 >> 16) << 16 | lsw10 & 65535;
+ var y9 = HASH[1];
+ var lsw11 = (b & 65535) + (y9 & 65535);
+ HASH[1] = (b >> 16) + (y9 >> 16) + (lsw11 >> 16) << 16 | lsw11 & 65535;
+ var y10 = HASH[2];
+ var lsw12 = (c & 65535) + (y10 & 65535);
+ HASH[2] = (c >> 16) + (y10 >> 16) + (lsw12 >> 16) << 16 | lsw12 & 65535;
+ var y11 = HASH[3];
+ var lsw13 = (d & 65535) + (y11 & 65535);
+ HASH[3] = (d >> 16) + (y11 >> 16) + (lsw13 >> 16) << 16 | lsw13 & 65535;
+ var y12 = HASH[4];
+ var lsw14 = (e & 65535) + (y12 & 65535);
+ HASH[4] = (e >> 16) + (y12 >> 16) + (lsw14 >> 16) << 16 | lsw14 & 65535;
+ var y13 = HASH[5];
+ var lsw15 = (f & 65535) + (y13 & 65535);
+ HASH[5] = (f >> 16) + (y13 >> 16) + (lsw15 >> 16) << 16 | lsw15 & 65535;
+ var y14 = HASH[6];
+ var lsw16 = (g & 65535) + (y14 & 65535);
+ HASH[6] = (g >> 16) + (y14 >> 16) + (lsw16 >> 16) << 16 | lsw16 & 65535;
+ var y15 = HASH[7];
+ var lsw17 = (h & 65535) + (y15 & 65535);
+ HASH[7] = (h >> 16) + (y15 >> 16) + (lsw17 >> 16) << 16 | lsw17 & 65535;
+ i += 16;
+ }
+ return HASH;
+ }
+ ,hex: function(a) {
+ var str = "";
+ var _g = 0;
+ while(_g < a.length) str += StringTools.hex(a[_g++],8);
+ return str.toLowerCase();
+ }
+};
+var haxe_ds_StringMap = function() {
+ this.h = { };
+};
+haxe_ds_StringMap.__name__ = true;
+haxe_ds_StringMap.prototype = {
+ setReserved: function(key,value) {
+ if(this.rh == null) {
+ this.rh = { };
+ }
+ this.rh["$" + key] = value;
+ }
+ ,getReserved: function(key) {
+ if(this.rh == null) {
+ return null;
+ } else {
+ return this.rh["$" + key];
+ }
+ }
+};
var haxe_http_HttpBase = function(url) {
this.url = url;
this.headers = [];
@@ -2009,6 +2193,44 @@ var haxe_io_Bytes = function(data) {
data.bytes = this.b;
};
haxe_io_Bytes.__name__ = true;
+haxe_io_Bytes.ofString = function(s,encoding) {
+ if(encoding == haxe_io_Encoding.RawNative) {
+ var buf = new Uint8Array(s.length << 1);
+ var _g = 0;
+ var _g1 = s.length;
+ while(_g < _g1) {
+ var i = _g++;
+ var c = s.charCodeAt(i);
+ buf[i << 1] = c & 255;
+ buf[i << 1 | 1] = c >> 8;
+ }
+ return new haxe_io_Bytes(buf.buffer);
+ }
+ var a = [];
+ var i1 = 0;
+ while(i1 < s.length) {
+ var c1 = s.charCodeAt(i1++);
+ if(55296 <= c1 && c1 <= 56319) {
+ c1 = c1 - 55232 << 10 | s.charCodeAt(i1++) & 1023;
+ }
+ if(c1 <= 127) {
+ a.push(c1);
+ } else if(c1 <= 2047) {
+ a.push(192 | c1 >> 6);
+ a.push(128 | c1 & 63);
+ } else if(c1 <= 65535) {
+ a.push(224 | c1 >> 12);
+ a.push(128 | c1 >> 6 & 63);
+ a.push(128 | c1 & 63);
+ } else {
+ a.push(240 | c1 >> 18);
+ a.push(128 | c1 >> 12 & 63);
+ a.push(128 | c1 >> 6 & 63);
+ a.push(128 | c1 & 63);
+ }
+ }
+ return new haxe_io_Bytes(new Uint8Array(a).buffer);
+};
haxe_io_Bytes.ofData = function(b) {
var hb = b.hxBytes;
if(hb != null) {
@@ -2242,10 +2464,10 @@ js_youtube_Youtube.init = function(onAPIReady) {
function $getIterator(o) { if( o instanceof Array ) return HxOverrides.iter(o); else return o.iterator(); }
function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; }
$global.$haxeUID |= 0;
-var __map_reserved = {};
if( String.fromCodePoint == null ) String.fromCodePoint = function(c) { return c < 0x10000 ? String.fromCharCode(c) : String.fromCharCode((c>>10)+0xD7C0)+String.fromCharCode((c&0x3FF)+0xDC00); }
String.__name__ = true;
Array.__name__ = true;
+var __map_reserved = {};
Object.defineProperty(js__$Boot_HaxeError.prototype,"message",{ get : function() {
return String(this.val);
}});
diff --git a/res/index.html b/res/index.html
index bbc2751..049600f 100644
--- a/res/index.html
+++ b/res/index.html
@@ -71,6 +71,9 @@
<div class="input-group" id="guestlogin" style="display: none;"><span class="input-group-addon">${enterAsGuest}</span>
<input class="form-control" id="guestname" type="text" placeholder="${yourName}">
</div>
+ <div class="input-group" id="guestpassword" style="display: none;"><span class="input-group-addon">${enterUserPassword}</span>
+ <input class="form-control" id="guestpass" type="text" placeholder="${yourPassword}">
+ </div>
</div>
<!-- <div class="gutter gutter-horizontal" style="width: 10px;"></div> -->
<div class="split split-horizontal" id="videowrap" style="width: calc(60% - 5px);">
diff --git a/src/Types.hx b/src/Types.hx
index 8eda528..49e93e4 100644
--- a/src/Types.hx
+++ b/src/Types.hx
@@ -15,9 +15,20 @@ typedef Config = {
videoLimit:Int,
leaderRequest:String,
emotes:Array<Emote>,
- filters:Array<Filter>
+ filters:Array<Filter>,
+ ?salt:String
};
+typedef UserList = {
+ admins:Array<UserField>,
+ ?salt:String
+}
+
+typedef UserField = {
+ name:String,
+ hash:String
+}
+
typedef Emote = {
name:String,
image:String
@@ -58,6 +69,7 @@ typedef WsEvent = {
},
?login:{
clientName:String,
+ ?passHash:String,
?clients:Array<ClientData>,
?isUnknownClient:Bool,
},
@@ -119,6 +131,7 @@ typedef WsEvent = {
enum abstract WsEventType(String) {
var Connected;
var Login;
+ var PasswordRequest;
var LoginError;
var Logout;
var Message;
diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx
index 51ba75d..e567331 100644
--- a/src/client/Buttons.hx
+++ b/src/client/Buttons.hx
@@ -209,13 +209,12 @@ class Buttons {
static function initChatInput(main:Main):Void {
final guestName:InputElement = cast ge("#guestname");
guestName.onkeydown = e -> {
- if (guestName.value.length == 0) return;
- if (e.keyCode == 13) main.send({
- type: Login,
- login: {
- clientName: guestName.value
- }
- });
+ if (e.keyCode == 13) main.guestLogin(guestName.value);
+ }
+
+ final guestPass:InputElement = cast ge("#guestpass");
+ guestPass.onkeydown = e -> {
+ if (e.keyCode == 13) main.userLogin(guestName.value, guestPass.value);
}
final chatLine:InputElement = cast ge("#chatline");
diff --git a/src/client/Main.hx b/src/client/Main.hx
index c74a600..9d383bc 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -1,5 +1,6 @@
package client;
+import haxe.crypto.Sha256;
import haxe.Timer;
import js.html.MouseEvent;
import js.html.KeyboardEvent;
@@ -217,10 +218,14 @@ class Main {
case Login:
onLogin(data.login.clients, data.login.clientName);
+ case PasswordRequest:
+ showGuestPasswordPanel();
+
case LoginError:
final text = Lang.get("usernameError")
.replace("$MAX", '${config.maxLoginLength}');
serverMessage(4, text);
+ showGuestLoginPanel();
case Logout:
updateClients(data.logout.clients);
@@ -324,12 +329,12 @@ class Main {
onLogin(connected.clients, connected.clientName);
}
final guestName:InputElement = cast ge("#guestname");
- if (guestName.value.length > 0) send({
- type: Login,
- login: {
- clientName: guestName.value
- }
- });
+ final guestPass:InputElement = cast ge("#guestpass");
+ if (config.salt != null && guestPass.value.length > 0) {
+ userLogin(guestName.value, guestPass.value);
+ } else {
+ guestLogin(guestName.value);
+ }
clearChat();
serverMessage(1);
for (message in connected.history) {
@@ -338,6 +343,27 @@ class Main {
player.setItems(connected.videoList, connected.itemPos);
}
+ public function guestLogin(name:String):Void {
+ if (name.length == 0) return;
+ send({
+ type: Login, login: {
+ clientName: name
+ }
+ });
+ }
+
+ public function userLogin(name:String, password:String):Void {
+ if (config.salt == null) return;
+ if (password.length == 0) return;
+ if (name.length == 0) return;
+ send({
+ type: Login, login: {
+ clientName: name,
+ passHash: Sha256.encode(password + config.salt)
+ }
+ });
+ }
+
function setConfig(config:Config):Void {
this.config = config;
pageTitle = config.channelName;
@@ -386,6 +412,7 @@ class Main {
function showGuestLoginPanel():Void {
ge("#guestlogin").style.display = "block";
+ ge("#guestpassword").style.display = "none";
ge("#chatline").style.display = "none";
ge("#exitBtn").textContent = Lang.get("login");
Browser.window.dispatchEvent(new Event("resize"));
@@ -393,12 +420,19 @@ class Main {
function hideGuestLoginPanel():Void {
ge("#guestlogin").style.display = "none";
+ ge("#guestpassword").style.display = "none";
ge("#chatline").style.display = "block";
ge("#exitBtn").textContent = Lang.get("exit");
if (isAdmin()) ge("#clearchatbtn").style.display = "inline-block";
Browser.window.dispatchEvent(new Event("resize"));
}
+ function showGuestPasswordPanel():Void {
+ ge("#guestlogin").style.display = "none";
+ ge("#chatline").style.display = "none";
+ ge("#guestpassword").style.display = "block";
+ }
+
function updateClients(newClients:Array<ClientData>):Void {
clients.resize(0);
for (client in newClients) {
diff --git a/src/server/ConsoleInput.hx b/src/server/ConsoleInput.hx
new file mode 100644
index 0000000..8b2463f
--- /dev/null
+++ b/src/server/ConsoleInput.hx
@@ -0,0 +1,59 @@
+package server;
+
+import js.html.Console;
+import js.node.Readline;
+import js.Node.process;
+using StringTools;
+
+class ConsoleInput {
+
+ final main:Main;
+
+ public function new(main:Main) {
+ this.main = main;
+ }
+
+ public function initConsoleInput():Void {
+ final rl = Readline.createInterface(process.stdin, process.stdout);
+ haxe.Log.trace = (msg, ?pos) -> {
+ Readline.clearLine(process.stdout, 0);
+ Readline.cursorTo(process.stdout, 0, null);
+ Console.log(msg);
+ rl.prompt(true);
+ };
+ rl.prompt();
+ rl.on("line", line -> {
+ parseLine(line);
+ rl.prompt();
+ });
+ // rl.on("close", exit);
+ }
+
+ function parseLine(line:String):Void {
+ if (line.startsWith("/addAdmin")) {
+ final args = line.split(" ");
+ if (args.length != 3) {
+ trace("Wrong count of arguments");
+ return;
+ }
+ final name = args[1];
+ final password = args[2];
+ if (main.badNickName(name)) {
+ final error = Lang.get("usernameError")
+ .replace("$MAX", '${main.config.maxLoginLength}');
+ trace(error);
+ return;
+ }
+ main.addAdmin(name, password);
+
+ } else if (line == "/exit") {
+ main.exit();
+ return;
+ } else {
+ trace('Unknown command "$line". List:
+/addAdmin name password | Adds channel admin
+/exit | Exit process');
+ }
+ }
+
+}
diff --git a/src/server/Main.hx b/src/server/Main.hx
index aaf9277..424f22f 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -1,5 +1,6 @@
package server;
+import haxe.crypto.Sha256;
import js.lib.Date;
import sys.FileSystem;
import sys.io.File;
@@ -25,9 +26,11 @@ class Main {
final localIp:String;
var globalIp:String;
final port:Int;
- final config:Config;
+ public final config:Config;
+ final userList:UserList;
final clients:Array<Client> = [];
final freeIds:Array<Int> = [];
+ final consoleInput:ConsoleInput;
final videoList = new VideoList();
final videoTimer = new VideoTimer();
final messages:Array<Message> = [];
@@ -39,10 +42,6 @@ class Main {
final envPort = (process.env : Dynamic).PORT;
if (envPort != null) port = envPort;
statePath = '$rootDir/user/state.json';
- function exit() {
- saveState();
- process.exit();
- }
// process.on("exit", exit);
process.on("SIGINT", exit); // ctrl+c
process.on("SIGUSR1", exit); // kill pid
@@ -59,9 +58,13 @@ class Main {
logError("unhandledRejection", reason);
exit();
});
+ consoleInput = new ConsoleInput(this);
+ consoleInput.initConsoleInput();
initIntergationHandlers();
loadState();
- config = getUserConfig();
+ config = loadUserConfig();
+ userList = loadUsers();
+ config.salt = generateConfigSalt();
localIp = Utils.getLocalIp();
globalIp = localIp;
this.port = port;
@@ -84,7 +87,18 @@ class Main {
wss.on("connection", onConnect);
}
- function getUserConfig():Config {
+ public function exit():Void {
+ saveState();
+ process.exit();
+ }
+
+ function generateConfigSalt():String {
+ if (userList.salt == null)
+ userList.salt = Sha256.encode('${Math.random()}');
+ return userList.salt;
+ }
+
+ function loadUserConfig():Config {
final config:Config = Json.parse(File.getContent('$rootDir/default-config.json'));
final customPath = '$rootDir/user/config.json';
if (!FileSystem.exists(customPath)) return config;
@@ -96,6 +110,23 @@ class Main {
return config;
}
+ function loadUsers():UserList {
+ final customPath = '$rootDir/user/users.json';
+ if (!FileSystem.exists(customPath)) return {
+ admins: []
+ };
+ return Json.parse(File.getContent(customPath));
+ }
+
+ function writeUsers(users:UserList):Void {
+ final folder = '$rootDir/user';
+ if (!FileSystem.exists(folder)) {
+ FileSystem.createDirectory(folder);
+ }
+ final data = Json.stringify(users, "\t");
+ File.saveContent('$folder/users.json', data);
+ }
+
function saveState():Void {
trace("Saving state...");
final data:ServerState = {
@@ -146,6 +177,18 @@ class Main {
}
}
+ public function addAdmin(name:String, password:String):Void {
+ password += config.salt;
+ final hash = Sha256.encode(password);
+ if (userList.admins == null) userList.admins = [];
+ userList.admins.push({
+ name: name,
+ hash: hash
+ });
+ writeUsers(userList);
+ trace('Admin $name added.');
+ }
+
function onConnect(ws:WebSocket, req:IncomingMessage):Void {
final ip = req.connection.remoteAddress;
final id = freeIds.length > 0 ? freeIds.shift() : clients.length;
@@ -153,7 +196,7 @@ class Main {
trace('$name connected ($ip)');
final isAdmin = req.connection.localAddress == ip;
final client = new Client(ws, req, id, name, 0);
- if (isAdmin) client.group.set(Admin);
+ client.isAdmin = isAdmin;
clients.push(client);
if (clients.length == 1 && videoList.length > 0)
if (videoTimer.isPaused()) videoTimer.play();
@@ -205,6 +248,22 @@ class Main {
send(client, {type: LoginError});
return;
}
+ final hash = data.login.passHash;
+ if (hash == null) {
+ if (userList.admins.exists(a -> a.name == name)) {
+ send(client, {type: PasswordRequest});
+ return;
+ }
+ } else {
+ if (userList.admins.exists(
+ a -> a.name == name && a.hash == hash
+ )) client.isAdmin = true;
+ else {
+ // TODO server msg type
+ send(client, {type: LoginError});
+ return;
+ }
+ }
client.name = name;
client.isUser = true;
send(client, {
@@ -217,6 +276,7 @@ class Main {
});
sendClientList();
+ case PasswordRequest:
case LoginError:
case Logout:
final oldName = client.name;
@@ -425,7 +485,7 @@ class Main {
final htmlChars = ~/[&^<>'"]/;
- function badNickName(name:String):Bool {
+ public function badNickName(name:String):Bool {
if (name.length == 0) return true;
if (htmlChars.match(name)) return true;
return false;
diff --git a/user/README.md b/user/README.md
index f59e576..e244d09 100644
--- a/user/README.md
+++ b/user/README.md
@@ -12,7 +12,12 @@ File example:
You can patch any file you want in project `res/` by creating `user/res/sameName` files.
For example `user/res/index.html` or `user/res/css/custom.css`.
You can also add any new files.
+
+## Server commands
+Server has input commands, for example, to set admin users. Simple enter anything to terminal after run to get command list.
+
## Other files here
-- `state.json` - saved state of latest server session (messages, videos, video time).
-- `logs/` - latest 10 logs. You can change count in config.
-- `crashes/` - folder with latest error logs, when the server had to restart itself.
+- `state.json` - Saved state of latest server session (messages, videos, video time).
+- `users.json` - Admin names with password hashes and random channel-specific salt. Do not share this file!
+- `crashes/` - Latest error logs, when the server had to restart itself.
+- `logs/` - (TODO) Latest activity logs.
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage