Updated the project structure, for the upcoming client (Still needs some work)
|
@ -1,4 +1,5 @@
|
|||
js/**/*.js*
|
||||
**.js*
|
||||
*.css.map
|
||||
asm/build/
|
||||
generated/
|
||||
node_modules/
|
||||
|
|
|
@ -1,458 +0,0 @@
|
|||
// Read an INI file into easy-to-access name/value pairs.
|
||||
|
||||
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
|
||||
// Go to the project home page for more info:
|
||||
//
|
||||
// https://github.com/benhoyt/inih
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __INI_H__
|
||||
#define __INI_H__
|
||||
|
||||
/* Make this header file easier to include in C++ code */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Typedef for prototype of handler function. */
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value);
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||
|
||||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||
is "" if name=value pair parsed before any section heading. name:value
|
||||
pairs are also supported as a concession to Python's configparser.
|
||||
|
||||
For each name=value pair parsed, call handler function with given user
|
||||
pointer as well as section, name, and value (data only valid for duration
|
||||
of handler call). Handler should return nonzero on success, zero on error.
|
||||
|
||||
Returns 0 on success, line number of first error on parse error (doesn't
|
||||
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||
error (only when INI_USE_STACK is zero).
|
||||
*/
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||
close the file when it's finished -- the caller must do that. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||
|
||||
inline int ini_parse_message(const std::string& message, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||
filename. Used for implementing custom or string-based I/O. */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user);
|
||||
|
||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||
configparser. If allowed, ini_parse() will call the handler with the same
|
||||
name for each subsequent line parsed. */
|
||||
#ifndef INI_ALLOW_MULTILINE
|
||||
#define INI_ALLOW_MULTILINE 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
|
||||
#ifndef INI_ALLOW_BOM
|
||||
#define INI_ALLOW_BOM 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||
Python 3.2+ configparser behaviour. */
|
||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||
#endif
|
||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack, zero to use heap (malloc/free). */
|
||||
#ifndef INI_USE_STACK
|
||||
#define INI_USE_STACK 1
|
||||
#endif
|
||||
|
||||
/* Stop parsing on first error (default is to keep parsing). */
|
||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||
#define INI_STOP_ON_FIRST_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file. */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
|
||||
#if !INI_USE_STACK
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#define MAX_SECTION 50
|
||||
#define MAX_NAME 50
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||
inline static char* rstrip(char* s)
|
||||
{
|
||||
char* p = s + strlen(s);
|
||||
while (p > s && isspace((unsigned char)(*--p)))
|
||||
*p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return pointer to first non-whitespace char in given string. */
|
||||
inline static char* lskip(const char* s)
|
||||
{
|
||||
while (*s && isspace((unsigned char)(*s)))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to null at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
inline static char* find_chars_or_comment(const char* s, const char* chars)
|
||||
{
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
#else
|
||||
while (*s && (!chars || !strchr(chars, *s))) {
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
||||
inline static char* strncpy0(char* dest, const char* src, size_t size)
|
||||
{
|
||||
strncpy(dest, src, size);
|
||||
dest[size - 1] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user)
|
||||
{
|
||||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
#if INI_USE_STACK
|
||||
char line[INI_MAX_LINE];
|
||||
#else
|
||||
char* line;
|
||||
#endif
|
||||
char section[MAX_SECTION] = "";
|
||||
char prev_name[MAX_NAME] = "";
|
||||
|
||||
char* start;
|
||||
char* end;
|
||||
char* name;
|
||||
char* value;
|
||||
int lineno = 0;
|
||||
int error = 0;
|
||||
|
||||
#if !INI_USE_STACK
|
||||
line = (char*)malloc(INI_MAX_LINE);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, INI_MAX_LINE, stream) != NULL) {
|
||||
lineno++;
|
||||
|
||||
start = line;
|
||||
#if INI_ALLOW_BOM
|
||||
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||
(unsigned char)start[1] == 0xBB &&
|
||||
(unsigned char)start[2] == 0xBF) {
|
||||
start += 3;
|
||||
}
|
||||
#endif
|
||||
start = lskip(rstrip(start));
|
||||
|
||||
if (*start == ';' || *start == '#') {
|
||||
/* Per Python configparser, allow both ; and # comments at the
|
||||
start of a line */
|
||||
}
|
||||
#if INI_ALLOW_MULTILINE
|
||||
else if (*prev_name && *start && start > line) {
|
||||
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(start, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
rstrip(start);
|
||||
#endif
|
||||
|
||||
/* Non-blank line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python configparser). */
|
||||
if (!handler(user, section, prev_name, start) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
#endif
|
||||
else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
strncpy0(section, start + 1, sizeof(section));
|
||||
*prev_name = '\0';
|
||||
}
|
||||
else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
value = lskip(end + 1);
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(value, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
#endif
|
||||
rstrip(value);
|
||||
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
strncpy0(prev_name, name, sizeof(prev_name));
|
||||
if (!handler(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
else if (!error) {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
|
||||
#if INI_STOP_ON_FIRST_ERROR
|
||||
if (error)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !INI_USE_STACK
|
||||
free(line);
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||
{
|
||||
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse_message(const std::string& message, ini_handler handler, void* user)
|
||||
{
|
||||
std::istringstream stream(message);
|
||||
return ini_parse_stream((ini_reader) [](char* str, int num, void* ptrStream) {
|
||||
auto stream = (std::istringstream*) ptrStream;
|
||||
stream->getline(str, num);
|
||||
return !!*stream ? str : nullptr;
|
||||
}, &stream, handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||
{
|
||||
FILE* file;
|
||||
int error;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
error = ini_parse_file(file, handler, user);
|
||||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif /* __INI_H__ */
|
||||
|
||||
|
||||
#ifndef __INIREADER_H__
|
||||
#define __INIREADER_H__
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
|
||||
// for simplicity here rather than speed, but it should be pretty decent.)
|
||||
class INIReader
|
||||
{
|
||||
public:
|
||||
// Empty Constructor
|
||||
INIReader() {};
|
||||
|
||||
// Construct INIReader and parse given filename. See ini.h for more info
|
||||
// about the parsing.
|
||||
INIReader(std::string filename, bool raw = false);
|
||||
|
||||
// Return the result of ini_parse(), i.e., 0 on success, line number of
|
||||
// first error on parse error, or -1 on file open error.
|
||||
int ParseError() const;
|
||||
|
||||
// Return the list of sections found in ini file
|
||||
std::set<std::string> Sections();
|
||||
|
||||
// Get a string value from INI file, returning default_value if not found.
|
||||
std::string Get(std::string section, std::string name,
|
||||
std::string default_value);
|
||||
|
||||
// Get an integer (long) value from INI file, returning default_value if
|
||||
// not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
|
||||
long GetInteger(std::string section, std::string name, long default_value);
|
||||
|
||||
// Get a real (floating point double) value from INI file, returning
|
||||
// default_value if not found or not a valid floating point value
|
||||
// according to strtod().
|
||||
double GetReal(std::string section, std::string name, double default_value);
|
||||
|
||||
// Get a boolean value from INI file, returning default_value if not found or if
|
||||
// not a valid true/false value. Valid true values are "true", "yes", "on", "1",
|
||||
// and valid false values are "false", "no", "off", "0" (not case sensitive).
|
||||
bool GetBoolean(std::string section, std::string name, bool default_value);
|
||||
|
||||
private:
|
||||
int _error;
|
||||
std::map<std::string, std::string> _values;
|
||||
std::set<std::string> _sections;
|
||||
static std::string MakeKey(std::string section, std::string name);
|
||||
static int ValueHandler(void* user, const char* section, const char* name,
|
||||
const char* value);
|
||||
};
|
||||
|
||||
#endif // __INIREADER_H__
|
||||
|
||||
|
||||
#ifndef __INIREADER__
|
||||
#define __INIREADER__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
|
||||
using std::string;
|
||||
|
||||
inline INIReader::INIReader(string data, bool raw)
|
||||
{
|
||||
if(raw)
|
||||
_error = ini_parse_message(data, ValueHandler, this);
|
||||
else
|
||||
_error = ini_parse(data.c_str(), ValueHandler, this);
|
||||
}
|
||||
|
||||
inline int INIReader::ParseError() const
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
inline std::set<string> INIReader::Sections()
|
||||
{
|
||||
return _sections;
|
||||
}
|
||||
|
||||
inline string INIReader::Get(string section, string name, string default_value)
|
||||
{
|
||||
string key = MakeKey(section, name);
|
||||
return _values.count(key) ? _values[key] : default_value;
|
||||
}
|
||||
|
||||
inline long INIReader::GetInteger(string section, string name, long default_value)
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
const char* value = valstr.c_str();
|
||||
char* end;
|
||||
// This parses "1234" (decimal) and also "0x4D2" (hex)
|
||||
long n = strtol(value, &end, 0);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
inline double INIReader::GetReal(string section, string name, double default_value)
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
const char* value = valstr.c_str();
|
||||
char* end;
|
||||
double n = strtod(value, &end);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
inline bool INIReader::GetBoolean(string section, string name, bool default_value)
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
// Convert to lower case to make string comparisons case-insensitive
|
||||
std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
|
||||
if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
|
||||
return true;
|
||||
else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
|
||||
return false;
|
||||
else
|
||||
return default_value;
|
||||
}
|
||||
|
||||
inline string INIReader::MakeKey(string section, string name)
|
||||
{
|
||||
string key = section + "=" + name;
|
||||
// Convert to lower case to make section/name lookups case-insensitive
|
||||
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
|
||||
return key;
|
||||
}
|
||||
|
||||
inline int INIReader::ValueHandler(void* user, const char* section, const char* name,
|
||||
const char* value)
|
||||
{
|
||||
INIReader* reader = (INIReader*)user;
|
||||
string key = MakeKey(section, name);
|
||||
if (reader->_values[key].size() > 0)
|
||||
reader->_values[key] += "\n";
|
||||
reader->_values[key] += value;
|
||||
reader->_sections.insert(section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // __INIREADER__
|
|
@ -1,283 +0,0 @@
|
|||
#include "Identity.h"
|
||||
#include <iostream>
|
||||
#include "base64.h"
|
||||
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#define ECC_TYPE_INDEX 5
|
||||
|
||||
static const char *TSKEY =
|
||||
"b9dfaa7bee6ac57ac7b65f1094a1c155"
|
||||
"e747327bc2fe5d51c512023fe54a2802"
|
||||
"01004e90ad1daaae1075d53b7d571c30"
|
||||
"e063b5a62a4a017bb394833aa0983e6e";
|
||||
|
||||
using namespace std;
|
||||
|
||||
inline int SHA1(const char* input, size_t length, char* result) {
|
||||
hash_state ctx = {};
|
||||
if (sha1_init(&ctx) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
if (sha1_process(&ctx, (uint8_t*) input, length) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
if (sha1_done(&ctx, (uint8_t*) result) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decriptIdentity(char *data, uint32_t length) {
|
||||
int dataSize = std::min((uint32_t) 100, length);
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
data[i] ^= TSKEY[i];
|
||||
}
|
||||
|
||||
char hash[SHA_DIGEST_LENGTH];
|
||||
//if(SHA1(data + 20, strlen(data + 20), hash) < 0) return -1;
|
||||
|
||||
hash_state ctx = {};
|
||||
if (sha1_init(&ctx) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
data[i] ^= hash[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encriptIdentity(char *data, uint32_t length) {
|
||||
char hash[SHA_DIGEST_LENGTH];
|
||||
//if(SHA1(data, length, hash) < 0) return -1;
|
||||
|
||||
hash_state ctx;
|
||||
if (sha1_init(&ctx) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
|
||||
{ return -1; }
|
||||
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
data[i] ^= hash[i];
|
||||
}
|
||||
|
||||
int dataSize = std::min((uint32_t) 100, length);
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
data[i] ^= TSKEY[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
Identity* Identity::createNew() {
|
||||
auto result = new Identity();
|
||||
|
||||
prng_state rndState = {};
|
||||
memset(&rndState, 0, sizeof(prng_state));
|
||||
int err;
|
||||
|
||||
result->keyPair = new ecc_key;
|
||||
|
||||
//cout << " -> " << find_prng("sprng") << endl;
|
||||
if((err = ecc_make_key_ex(&rndState, find_prng("sprng"), result->keyPair, <c_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK){
|
||||
printf("Cant create a new identity (Keygen)\n");
|
||||
printf("Message: %s\n", error_to_string(err));
|
||||
delete result;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Identity* Identity::parse(const std::string& data, std::string& error) {
|
||||
int vindex = data.find('V');
|
||||
if(vindex <= 0) {
|
||||
error = "Invalid structure";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto slevel = data.substr(0, vindex);
|
||||
if(slevel.find_first_not_of("0123456789") != std::string::npos) {
|
||||
error = "Invalid offset (" + slevel + ")";
|
||||
return nullptr;
|
||||
}
|
||||
mp_int keyOffset{};
|
||||
mp_init(&keyOffset);
|
||||
mp_read_radix(&keyOffset, slevel.data(), 10);
|
||||
|
||||
auto keyData = data.substr(vindex + 1);
|
||||
keyData = base64::decode(keyData);
|
||||
if(encriptIdentity(&keyData[0], keyData.length()) < 0) {
|
||||
error = "Could not decrypt key";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto identity = new Identity(base64::decode(keyData), keyOffset, keyOffset);
|
||||
if(!identity->keyPair) {
|
||||
error = "Could not load key";
|
||||
delete identity;
|
||||
return nullptr;
|
||||
}
|
||||
printf("X: %s | %s\n", slevel.c_str(), identity->lastValidKeyOffsetString().c_str());
|
||||
return identity;
|
||||
}
|
||||
|
||||
Identity::Identity(const std::string& asnStruct, mp_int keyOffset, mp_int lastCheckedOffset) {
|
||||
this->keyOffset = keyOffset;
|
||||
this->lastCheckedOffset = lastCheckedOffset;
|
||||
importKey(asnStruct);
|
||||
|
||||
mp_init_copy(&this->keyOffset, &keyOffset);
|
||||
mp_init_copy(&this->lastCheckedOffset, &lastCheckedOffset);
|
||||
}
|
||||
|
||||
Identity::Identity() {
|
||||
mp_init_multi(&this->keyOffset, &this->lastCheckedOffset, nullptr);
|
||||
this->keyPair = nullptr;
|
||||
}
|
||||
|
||||
Identity::~Identity() {
|
||||
delete this->keyPair;
|
||||
this->keyPair = nullptr;
|
||||
|
||||
mp_clear_multi(&this->keyOffset, &this->lastCheckedOffset, nullptr);
|
||||
}
|
||||
|
||||
void Identity::importKey(std::string asnStruct) {
|
||||
this->keyPair = new ecc_key;
|
||||
int err;
|
||||
if((err = ecc_import_ex((const unsigned char *) asnStruct.data(), asnStruct.length(), this->keyPair, <c_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK){
|
||||
delete this->keyPair;
|
||||
this->keyPair = nullptr;
|
||||
|
||||
printf("Cant import identity from asn structure\n");
|
||||
printf("Message: %s\n", error_to_string(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Identity::exportIdentity() {
|
||||
std::string data = privateKey();
|
||||
decriptIdentity((char *) data.data(), data.length());
|
||||
return this->lastValidKeyOffsetString() + "V" + base64_encode(data);
|
||||
}
|
||||
|
||||
std::string Identity::uid() {
|
||||
char buffer[SHA_DIGEST_LENGTH];
|
||||
auto key = this->publicKey();
|
||||
SHA1(key.data(), key.length(), buffer);
|
||||
return base64::encode(buffer, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
inline string hex(string input, char beg, char end){
|
||||
assert(end - beg == 16);
|
||||
|
||||
int len = input.length() * 2;
|
||||
char output[len];
|
||||
int idx = 0;
|
||||
for(int index = 0; index < input.length(); index++){
|
||||
char elm = input[index];
|
||||
output[idx++] = static_cast<char>(beg + ((elm >> 4) & 0x0F));
|
||||
output[idx++] = static_cast<char>(beg + ((elm & 0x0F) >> 0));
|
||||
}
|
||||
|
||||
return string(output, len);
|
||||
}
|
||||
|
||||
std::string Identity::avatarId() {
|
||||
return hex(base64::decode(this->uid()), 'a', 'q');
|
||||
}
|
||||
|
||||
std::string Identity::publicKey() {
|
||||
assert(this->keyPair);
|
||||
|
||||
ulong32 bufferLength = 1028;
|
||||
char buffer[bufferLength];
|
||||
ecc_export((unsigned char *) buffer, &bufferLength, PK_PUBLIC, this->keyPair);
|
||||
|
||||
return base64_encode(std::string(buffer, bufferLength));
|
||||
}
|
||||
|
||||
std::string Identity::privateKey() {
|
||||
assert(this->keyPair);
|
||||
|
||||
ulong32 bufferLength = 1028;
|
||||
char buffer[bufferLength];
|
||||
ecc_export((unsigned char *) buffer, &bufferLength, PK_PRIVATE, this->keyPair);
|
||||
|
||||
return base64_encode(std::string(buffer, bufferLength));
|
||||
}
|
||||
|
||||
ecc_key& Identity::getPrivateKey() {
|
||||
return *keyPair;
|
||||
}
|
||||
|
||||
#define MaxUlongString 20
|
||||
|
||||
bool Identity::improveSecurityLevel(int target) {
|
||||
auto publicKey = this->publicKey();
|
||||
char hashBuffer[publicKey.length() + MaxUlongString];
|
||||
memcpy(hashBuffer, publicKey.data(), publicKey.length());
|
||||
|
||||
if(mp_cmp(&this->lastCheckedOffset, &this->keyOffset) < 0)
|
||||
mp_copy(&this->keyOffset, &this->lastCheckedOffset);
|
||||
int best = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
|
||||
while(true){
|
||||
if(best >= target) return true;
|
||||
|
||||
int currentLevel = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
|
||||
if(currentLevel >= best){
|
||||
this->keyOffset = this->lastCheckedOffset;
|
||||
best = currentLevel;
|
||||
}
|
||||
mp_add_d(&this->lastCheckedOffset, 1, &this->lastCheckedOffset);
|
||||
}
|
||||
}
|
||||
|
||||
int Identity::getSecurityLevel() {
|
||||
auto length = publicKey().length();
|
||||
char hashBuffer[length + MaxUlongString];
|
||||
|
||||
auto publicKey = this->publicKey();
|
||||
memcpy(hashBuffer, publicKey.data(), publicKey.length());
|
||||
|
||||
return getSecurityLevel(hashBuffer, publicKey.length(), this->keyOffset);
|
||||
}
|
||||
|
||||
int Identity::getSecurityLevel(char *hashBuffer, size_t keyLength, mp_int offset) {
|
||||
char numBuffer[MaxUlongString];
|
||||
mp_todecimal(&offset, numBuffer);
|
||||
/*
|
||||
int numLen = 0;
|
||||
do {
|
||||
numBuffer[numLen] = '0' + (offset % 10);
|
||||
offset /= 10;
|
||||
numLen++;
|
||||
} while(offset > 0);
|
||||
for(int i = 0; i < numLen; i++)
|
||||
hashBuffer[keyLength + i] = numBuffer[numLen - (i + 1)];
|
||||
*/
|
||||
auto numLen = strlen(numBuffer);
|
||||
memcpy(&hashBuffer[keyLength], numBuffer, numLen);
|
||||
|
||||
char shaBuffer[SHA_DIGEST_LENGTH];
|
||||
SHA1(hashBuffer, keyLength + numLen, shaBuffer);
|
||||
|
||||
//Leading zero bits
|
||||
int zeroBits = 0;
|
||||
int i;
|
||||
for(i = 0; i < SHA_DIGEST_LENGTH; i++)
|
||||
if(shaBuffer[i] == 0) zeroBits += 8;
|
||||
else break;
|
||||
if(i < SHA_DIGEST_LENGTH)
|
||||
for(int bit = 0; bit < 8; bit++)
|
||||
if((shaBuffer[i] & (1 << bit)) == 0) zeroBits++;
|
||||
else break;
|
||||
return zeroBits;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <tomcrypt.h>
|
||||
#include <string>
|
||||
#include <tommath.h>
|
||||
|
||||
namespace ts {
|
||||
class Identity {
|
||||
inline std::string toString(const mp_int& num) {
|
||||
char buffer[1024];
|
||||
mp_todecimal(&num, buffer);
|
||||
return std::string(buffer);
|
||||
}
|
||||
public:
|
||||
static Identity* createNew();
|
||||
static Identity* parse(const std::string&, std::string&);
|
||||
|
||||
Identity(const std::string& asnStruct,mp_int keyOffset,mp_int lastCheckedOffset);
|
||||
~Identity();
|
||||
|
||||
bool valid(){ return keyPair != nullptr; }
|
||||
|
||||
std::string uid();
|
||||
std::string avatarId();
|
||||
|
||||
std::string publicKey();
|
||||
std::string privateKey();
|
||||
std::string exportIdentity();
|
||||
|
||||
ecc_key* getKeyPair(){
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
ecc_key& getPrivateKey();
|
||||
|
||||
bool improveSecurityLevel(int target);
|
||||
int getSecurityLevel();
|
||||
|
||||
mp_int lastValidKeyOffset(){ return keyOffset; }
|
||||
mp_int lastTestedKeyOffset(){ return lastCheckedOffset; }
|
||||
|
||||
std::string lastValidKeyOffsetString(){
|
||||
return toString(this->lastValidKeyOffset());
|
||||
}
|
||||
|
||||
std::string lastTestedKeyOffsetString(){
|
||||
return toString(this->lastTestedKeyOffset());
|
||||
}
|
||||
private:
|
||||
Identity();
|
||||
|
||||
int getSecurityLevel(char* hasBuffer, size_t keyLength, mp_int offset);
|
||||
void importKey(std::string asn1);
|
||||
|
||||
ecc_key* keyPair = nullptr;
|
||||
mp_int keyOffset;
|
||||
mp_int lastCheckedOffset;
|
||||
};
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
#include <cstdio>
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/bind.h>
|
||||
#include <tomcrypt.h>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include "Identity.h"
|
||||
#include "base64.h"
|
||||
#define INI_MAX_LINE 1024
|
||||
#include "INIReader.h"
|
||||
|
||||
using namespace emscripten;
|
||||
using namespace std;
|
||||
extern "C" {
|
||||
std::string errorMessage = "";
|
||||
|
||||
inline const char* cstr(const std::string& message) {
|
||||
auto buffer = (char*) malloc(message.length() + 1);
|
||||
cout << "Allocating at " << (void*) buffer << endl;
|
||||
buffer[message.length()] = '\0';
|
||||
memcpy(buffer, message.data(), message.length());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char* last_error_message() {
|
||||
return cstr(errorMessage);
|
||||
};
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void destroy_string(const char* str) {
|
||||
cout << "Deallocating at " << (void*) str << endl;
|
||||
if(str) free((void *) str);
|
||||
};
|
||||
|
||||
inline void clear_error() { errorMessage = ""; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int tomcrypt_initialize() {
|
||||
init_LTM();
|
||||
if(register_prng(&sprng_desc) == -1) {
|
||||
printf("could not setup prng\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (register_cipher(&rijndael_desc) == -1) {
|
||||
printf("could not setup rijndael\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cout << "Initialized!" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void* parse_identity(const char* input) {
|
||||
cout << "Got messsage: " << input << endl;
|
||||
clear_error();
|
||||
return ts::Identity::parse(input, errorMessage);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void* parse_identity_file(const char* input) {
|
||||
clear_error();
|
||||
INIReader reader(input, true);
|
||||
if(reader.ParseError() != 0) {
|
||||
errorMessage = "Could not parse file " + to_string(reader.ParseError());
|
||||
return nullptr;
|
||||
}
|
||||
auto identity = reader.Get("Identity", "identity", "");
|
||||
if(!identity.empty() && identity[0] == '"')
|
||||
identity = identity.substr(1);
|
||||
if(!identity.empty() && identity.back() == '"')
|
||||
identity = identity.substr(0, identity.length() - 1);
|
||||
if(identity.empty()) {
|
||||
errorMessage = "Mussing identity value at Identity::identity";
|
||||
return nullptr;
|
||||
}
|
||||
return ts::Identity::parse(identity, errorMessage);
|
||||
}
|
||||
|
||||
#define IDENTITIEFY(_ret) \
|
||||
auto identity = dynamic_cast<ts::Identity*>((ts::Identity*) ptrIdentity); \
|
||||
if(!identity) { \
|
||||
errorMessage = "Invalid identity pointer!"; \
|
||||
return _ret; \
|
||||
}
|
||||
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void delete_identity(void* ptrIdentity) {
|
||||
IDENTITIEFY(;);
|
||||
delete identity;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char* identity_security_level(void* ptrIdentity) {
|
||||
IDENTITIEFY("");
|
||||
return cstr(std::to_string(identity->getSecurityLevel()));
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char* identity_export(void* ptrIdentity) {
|
||||
IDENTITIEFY("");
|
||||
return cstr(identity->exportIdentity());
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char* identity_key_public(void* ptrIdentity) {
|
||||
IDENTITIEFY("");
|
||||
return cstr(identity->publicKey());
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char* identity_uid(void* ptrIdentity) {
|
||||
IDENTITIEFY("");
|
||||
return cstr(identity->uid());
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char* identity_sign(void* ptrIdentity, const char* message, int length) {
|
||||
IDENTITIEFY("");
|
||||
|
||||
ulong32 bufferLength = 128;
|
||||
char signBuffer[bufferLength];
|
||||
|
||||
prng_state rndState = {};
|
||||
memset(&rndState, 0, sizeof(prng_state));
|
||||
|
||||
auto state = ecc_sign_hash((const unsigned char*) message, length, reinterpret_cast<unsigned char *>(signBuffer), &bufferLength, &rndState, find_prng("sprng"), identity->getKeyPair());
|
||||
if(state != CRYPT_OK) {
|
||||
errorMessage = "Could not sign message (" + std::string(error_to_string(state)) + "|" + std::to_string(state) + ")";
|
||||
return "";
|
||||
}
|
||||
|
||||
return cstr(base64::encode(signBuffer, bufferLength));
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tomcrypt.h>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace base64 {
|
||||
/**
|
||||
* Encodes a given string in Base64
|
||||
* @param input The input string to Base64-encode
|
||||
* @param inputSize The size of the input to decode
|
||||
* @return A Base64-encoded version of the encoded string
|
||||
*/
|
||||
inline std::string encode(const char* input, const unsigned long inputSize) {
|
||||
auto outlen = static_cast<unsigned long>(inputSize + (inputSize / 3.0) + 16);
|
||||
auto outbuf = new unsigned char[outlen]; //Reserve output memory
|
||||
if(base64_encode((unsigned char*) input, inputSize, outbuf, &outlen) != CRYPT_OK){
|
||||
std::cerr << "Invalid input '" << input << "'" << std::endl;
|
||||
return "";
|
||||
}
|
||||
std::string ret((char*) outbuf, outlen);
|
||||
delete[] outbuf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a given string in Base64
|
||||
* @param input The input string to Base64-encode
|
||||
* @return A Base64-encoded version of the encoded string
|
||||
*/
|
||||
inline std::string encode(const std::string& input) { return encode(input.c_str(), input.size()); }
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a Base64-encoded string.
|
||||
* @param input The input string to decode
|
||||
* @return A string (binary) that represents the Base64-decoded data of the input
|
||||
*/
|
||||
inline std::string decode(const char* input, ulong32 size) {
|
||||
auto out = new unsigned char[size];
|
||||
if(base64_strict_decode((unsigned char*) input, size, out, &size) != CRYPT_OK){
|
||||
std::cerr << "Invalid base 64 string '" << input << "'" << std::endl;
|
||||
return "";
|
||||
}
|
||||
std::string ret((char*) out, size);
|
||||
delete[] out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a Base64-encoded string.
|
||||
* @param input The input string to decode
|
||||
* @return A string (binary) that represents the Base64-decoded data of the input
|
||||
*/
|
||||
inline std::string decode(const std::string& input) { return decode(input.c_str(), input.size()); }
|
||||
}
|
||||
inline std::string base64_encode(const char* input, const unsigned long inputSize) { return base64::encode(input, inputSize); }
|
||||
inline std::string base64_encode(const std::string& input) { return base64::encode(input.c_str(), input.size()); }
|
|
@ -1 +0,0 @@
|
|||
Subproject commit b97dd67fdc75a39d0fc99ceee573921ba3e73b1f
|
|
@ -1 +0,0 @@
|
|||
Subproject commit bc6d70bd1f4a6778cf51379090d8391dcf53590d
|
|
@ -1 +0,0 @@
|
|||
Subproject commit fe6cc64884b2b408b607389f46266d44fd942a79
|
132
asm/src/opus.cpp
|
@ -1,132 +0,0 @@
|
|||
#include <opus.h>
|
||||
#include <emscripten.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
extern "C" {
|
||||
struct OpusHandle {
|
||||
OpusEncoder* encoder = nullptr;
|
||||
OpusDecoder* decoder = nullptr;
|
||||
|
||||
size_t channelCount = 1;
|
||||
size_t sampleRate = 48000;
|
||||
int opusType = OPUS_APPLICATION_AUDIO;
|
||||
};
|
||||
|
||||
const char* opus_errors[7] = {
|
||||
"One or more invalid/out of range arguments", //-1 (OPUS_BAD_ARG)
|
||||
"Not enough bytes allocated in the buffer", //-2 (OPUS_BUFFER_TOO_SMALL)
|
||||
"An internal error was detected", //-3 (OPUS_INTERNAL_ERROR)
|
||||
"The compressed data passed is corrupted", //-4 (OPUS_INVALID_PACKET)
|
||||
"Invalid/unsupported request number", //-5 (OPUS_UNIMPLEMENTED)
|
||||
"An encoder or decoder structure is invalid or already freed", //-6 (OPUS_INVALID_STATE)
|
||||
"Memory allocation has failed" //-7 (OPUS_ALLOC_FAIL)
|
||||
};
|
||||
|
||||
inline const char* opus_error_message(int error) {
|
||||
error = abs(error);
|
||||
if(error > 0 && error <= 7) return opus_errors[error - 1];
|
||||
return "undefined error";
|
||||
}
|
||||
|
||||
inline int currentMillies() {
|
||||
return EM_ASM_INT({ return Date.now(); });
|
||||
}
|
||||
|
||||
#define _S(x) #x
|
||||
#define INVOKE_OPUS(result, method, ...) \
|
||||
result = method( __VA_ARGS__ ); \
|
||||
if(error != 0){ \
|
||||
printf("Got opus error while invoking %s. Code: %d Message: %s\n", _S(method), error, opus_error_message(error)); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
inline bool reinitialize_decoder(OpusHandle *handle) {
|
||||
if (handle->decoder)
|
||||
opus_decoder_destroy(handle->decoder);
|
||||
|
||||
int error = 0;
|
||||
INVOKE_OPUS(handle->decoder, opus_decoder_create, 48000, handle->channelCount, &error);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool reinitialize_encoder(OpusHandle *handle) {
|
||||
if (handle->encoder)
|
||||
opus_encoder_destroy(handle->encoder);
|
||||
|
||||
int error = 0;
|
||||
INVOKE_OPUS(handle->encoder, opus_encoder_create, 48000, handle->channelCount, handle->opusType, &error);
|
||||
INVOKE_OPUS(error, opus_encoder_ctl, handle->encoder, OPUS_SET_COMPLEXITY(1));
|
||||
//INVOKE_OPUS(error, opus_encoder_ctl, handle->encoder, OPUS_SET_BITRATE(4740));
|
||||
|
||||
EM_ASM(
|
||||
printMessageToServerTab('Encoder initialized!');
|
||||
printMessageToServerTab(' Comprexity: 1');
|
||||
printMessageToServerTab(' Bitrate: 4740');
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
OpusHandle* codec_opus_createNativeHandle(size_t channelCount, int type) {
|
||||
printf("Initialize opus. (Channel count: %d Sample rate: %d Type: %d)!\n", channelCount, 48000, type);
|
||||
auto codec = new OpusHandle{};
|
||||
codec->opusType = type;
|
||||
if(!reinitialize_decoder(codec)) return nullptr;
|
||||
if(!reinitialize_encoder(codec)) return nullptr;
|
||||
return codec;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void codec_opus_deleteNativeHandle(OpusHandle* codec) {
|
||||
if(!codec) return;
|
||||
|
||||
if(codec->decoder) opus_decoder_destroy(codec->decoder);
|
||||
codec->decoder = nullptr;
|
||||
|
||||
if(codec->encoder) opus_encoder_destroy(codec->encoder);
|
||||
codec->encoder = nullptr;
|
||||
|
||||
delete codec;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int codec_opus_encode(OpusHandle* handle, uint8_t* buffer, size_t length, size_t maxLength) {
|
||||
auto begin = currentMillies();
|
||||
auto result = opus_encode_float(handle->encoder, (float*) buffer, length / handle->channelCount, buffer, maxLength);
|
||||
if(result < 0) return result;
|
||||
auto end = currentMillies();
|
||||
EM_ASM({
|
||||
printMessageToServerTab("codec_opus_encode(...) tooks " + $0 + "ms to execute!");
|
||||
}, end - begin);
|
||||
return result;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int codec_opus_decode(OpusHandle* handle, uint8_t* buffer, size_t length, size_t maxLength) {
|
||||
auto result = opus_decode_float(handle->decoder, buffer, length, (float*) buffer, maxLength / sizeof(float) / handle->channelCount, false);
|
||||
if(result < 0) return result; //Failed
|
||||
return result;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int codec_opus_changeApplication(OpusHandle* handle, int type) {
|
||||
handle->opusType = type;
|
||||
if(type != OPUS_APPLICATION_VOIP && type != OPUS_APPLICATION_AUDIO && type != OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
return 1;
|
||||
return opus_encoder_ctl(handle->encoder, OPUS_SET_APPLICATION(type));
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int codec_opus_reset(OpusHandle* handle) {
|
||||
if(!reinitialize_decoder(handle)) return 0;
|
||||
if(!reinitialize_encoder(handle)) return 0;
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
|
||||
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
|
||||
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type));
|
||||
*/
|
||||
}
|
|
@ -239,15 +239,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (isset($_GET["type"])) {
|
||||
error_log("Got authX request!");
|
||||
var_dump($_GET);
|
||||
var_dump($_POST);
|
||||
if ($_GET["type"] == "login") {
|
||||
checkLogin($_POST["user"], $_POST["pass"]);
|
||||
} else if ($_GET["type"] == "logout") {
|
||||
logout();
|
||||
} else die("unknown type!");
|
||||
} else if(isset($_POST)) {
|
||||
error_log("Got auth request!");
|
||||
if(!$_INCLIDE_ONLY) {
|
||||
if (isset($_GET["type"])) {
|
||||
error_log("Got authX request!");
|
||||
var_dump($_GET);
|
||||
var_dump($_POST);
|
||||
if ($_GET["type"] == "login") {
|
||||
checkLogin($_POST["user"], $_POST["pass"]);
|
||||
} else if ($_GET["type"] == "logout") {
|
||||
logout();
|
||||
} else die("unknown type!");
|
||||
} else if(isset($_POST)) {
|
||||
error_log("Got auth request!");
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
const btn_login = $("#btn_login");
|
||||
btn_login.on('click', () => {
|
||||
btn_login
|
||||
.prop("disabled", true)
|
||||
.empty()
|
||||
.append($(document.createElement("i")).addClass("fa fa-circle-o-notch fa-spin"));
|
||||
submitLogin($("#user").val(), $("#pass").val());
|
||||
});
|
||||
function submitLogin(user, pass) {
|
||||
$.ajax({
|
||||
url: "auth.php?type=login",
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {
|
||||
user: user,
|
||||
pass: pass
|
||||
},
|
||||
success: (result) => {
|
||||
setTimeout(() => {
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(result);
|
||||
}
|
||||
catch (e) {
|
||||
loginFailed("Invalid response: " + result);
|
||||
return;
|
||||
}
|
||||
if (data["success"] == false) {
|
||||
loginFailed(data["msg"]);
|
||||
return;
|
||||
}
|
||||
if (data["allowed"] == false) {
|
||||
loginFailed("You're not allowed for the closed alpha!");
|
||||
return;
|
||||
}
|
||||
$("#login").hide(500);
|
||||
$("#success").show(500);
|
||||
document.cookie = data["sessionName"] + "=" + data["sessionId"] + ";path=/";
|
||||
document.cookie = data["cookie_name_data"] + "=" + data["user_data"] + ";path=/";
|
||||
document.cookie = data["cookie_name_sign"] + "=" + data["user_sign"] + ";path=/";
|
||||
console.log(result);
|
||||
setTimeout(() => {
|
||||
window.location.href = btn_login.attr("target");
|
||||
}, 1000 + Math.random() % 1500);
|
||||
}, 500 + Math.random() % 500);
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
loginFailed("Invalid request (" + status + ") => " + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
function loginFailed(err = "") {
|
||||
btn_login
|
||||
.prop("disabled", false)
|
||||
.empty()
|
||||
.append($(document.createElement("a")).text("Login"));
|
||||
let errTag = $(".box .error");
|
||||
if (err !== "") {
|
||||
errTag.text(err).show(500);
|
||||
}
|
||||
else
|
||||
errTag.hide(500);
|
||||
}
|
||||
//<i class="fa fa-circle-o-notch fa-spin" id="login-loader"></i>
|
||||
$("#user").on('keydown', event => {
|
||||
if (event.key == "Enter")
|
||||
$("#pass").focus();
|
||||
});
|
||||
$("#pass").on('keydown', event => {
|
||||
if (event.key == "Enter")
|
||||
$("#btn_login").trigger("click");
|
||||
});
|
||||
//# sourceMappingURL=auth.js.map
|
|
@ -0,0 +1,20 @@
|
|||
html, body {
|
||||
border: 0;
|
||||
margin: 0; }
|
||||
|
||||
.app-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center; }
|
||||
.app-container .app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
resize: both; }
|
||||
|
||||
/*# sourceMappingURL=main.css.map */
|
|
@ -0,0 +1,21 @@
|
|||
html, body {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
||||
display: flex; flex-direction: column; resize: both;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
/* native functions declaration */
|
||||
|
||||
declare function displayCriticalError(message: string);
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"mappings": "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",
|
||||
"sources": ["loader.scss"],
|
||||
"names": [],
|
||||
"file": "loader.css"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"mappings": "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",
|
||||
"sources": ["info_plate.scss"],
|
||||
"names": [],
|
||||
"file": "info_plate.css"
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
../../auth/js/auth.js
|
|
@ -1 +0,0 @@
|
|||
../../generated/js/client.min.js
|
|
@ -1 +0,0 @@
|
|||
../../../js/codec/CompiledCodecWorker.js
|
|
@ -1 +0,0 @@
|
|||
../../js/load.js
|
|
@ -1 +0,0 @@
|
|||
../../../js/workers/WorkerCodec.js
|
|
@ -0,0 +1,304 @@
|
|||
<?php
|
||||
$APP_FILE_LIST = [
|
||||
[
|
||||
"type" => "html",
|
||||
"search-pattern" => "/^([a-zA-Z]+)\.html$/",
|
||||
|
||||
"path" => "./",
|
||||
"local-path" => "./"
|
||||
],
|
||||
[
|
||||
"type" => "js",
|
||||
"search-pattern" => "/.*\.js$/",
|
||||
"search-exclude" => "/(.*\/)?workers\/.*/",
|
||||
|
||||
"path" => "js/",
|
||||
"local-path" => "./shared/js/"
|
||||
],
|
||||
[
|
||||
"type" => "js",
|
||||
"search-pattern" => "/WorkerCodec.js$/",
|
||||
|
||||
"path" => "js/workers/",
|
||||
"local-path" => "./shared/js/workers/"
|
||||
],
|
||||
[
|
||||
"type" => "css",
|
||||
"search-pattern" => "/.*\.css$/",
|
||||
|
||||
"path" => "css/",
|
||||
"local-path" => "./shared/css/"
|
||||
],
|
||||
[
|
||||
"type" => "img",
|
||||
"search-pattern" => "/.*\.(svg|png)/",
|
||||
|
||||
"path" => "img/",
|
||||
"local-path" => "./shared/img/"
|
||||
],
|
||||
[
|
||||
"type" => "wasm",
|
||||
"search-pattern" => "/.*\.(wasm)/",
|
||||
|
||||
"path" => "wasm/",
|
||||
"local-path" => "./asm/generated/"
|
||||
],
|
||||
[
|
||||
"type" => "js",
|
||||
"search-pattern" => "/.*\.(js)/",
|
||||
|
||||
"path" => "asm/generated/",
|
||||
"local-path" => "./asm/generated/"
|
||||
],
|
||||
|
||||
/* vendors */
|
||||
[
|
||||
"type" => "js",
|
||||
"search-pattern" => "/.*\.js$/",
|
||||
|
||||
"path" => "vendor/",
|
||||
"local-path" => "./vendor/"
|
||||
],
|
||||
[
|
||||
"type" => "css",
|
||||
"search-pattern" => "/.*\.css$/",
|
||||
|
||||
"path" => "vendor/",
|
||||
"local-path" => "./vendor/"
|
||||
],
|
||||
|
||||
/* client specs */
|
||||
[
|
||||
"client-only" => true,
|
||||
"type" => "css",
|
||||
"search-pattern" => "/.*\.css$/",
|
||||
|
||||
"path" => "css/",
|
||||
"local-path" => "./client/css/"
|
||||
],
|
||||
|
||||
/* web specs */
|
||||
[
|
||||
"web-only" => true,
|
||||
"type" => "css",
|
||||
"search-pattern" => "/.*\.css$/",
|
||||
|
||||
"path" => "css/",
|
||||
"local-path" => "./web/css/"
|
||||
],
|
||||
[
|
||||
"web-only" => true,
|
||||
"type" => "html",
|
||||
"search-pattern" => "/.*\.(php|html)/",
|
||||
|
||||
"path" => "./",
|
||||
"local-path" => "./web/html/"
|
||||
],
|
||||
[
|
||||
"web-only" => true,
|
||||
"type" => "html",
|
||||
"search-pattern" => "/.*\.(php|html)/",
|
||||
"search-exclude" => "/(files.php)/",
|
||||
"search-depth" => 1,
|
||||
|
||||
"path" => "./",
|
||||
"local-path" => "./"
|
||||
],
|
||||
];
|
||||
|
||||
function list_dir($base_dir, $match = null, $depth = -1, &$results = array(), $dir = "") {
|
||||
if($depth == 0) return $results;
|
||||
|
||||
$files = scandir($base_dir . $dir);
|
||||
|
||||
foreach($files as $key => $value){
|
||||
$path = $base_dir.$dir.DIRECTORY_SEPARATOR.$value;
|
||||
if(!is_dir($path)) {
|
||||
if(!$match || preg_match($match, ($dir ? $dir.DIRECTORY_SEPARATOR : "").$value))
|
||||
$results[] = ($dir ? $dir.DIRECTORY_SEPARATOR : "").$value;
|
||||
} else if($value != "." && $value != "..") {
|
||||
list_dir($base_dir, $match, $depth - 1, $results, ($dir ? $dir.DIRECTORY_SEPARATOR : "").$value);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
class AppFile {
|
||||
public $type;
|
||||
public $name;
|
||||
public $path;
|
||||
public $local_path;
|
||||
public $hash;
|
||||
}
|
||||
|
||||
function find_files($flag = 0b11) { //TODO Use cache here!
|
||||
global $APP_FILE_LIST;
|
||||
$result = [];
|
||||
|
||||
foreach ($APP_FILE_LIST as $entry) {
|
||||
if(isset($entry["web-only"]) && $entry["web-only"] && ($flag & 0b01) == 0) continue;
|
||||
if(isset($entry["client-only"]) && $entry["client-only"] && ($flag & 0b10) == 0) continue;
|
||||
|
||||
$entries = list_dir($entry["local-path"], $entry["search-pattern"], isset($entry["search-depth"]) ? $entry["search-depth"] : -1);
|
||||
foreach ($entries as $f_entry) {
|
||||
if(isset($entry["search-exclude"]) && preg_match($entry["search-exclude"], $f_entry)) continue;
|
||||
$file = new AppFile;
|
||||
|
||||
$idx_sep = strrpos($f_entry, DIRECTORY_SEPARATOR);
|
||||
$file->path = "./" . $entry["path"] . "/";
|
||||
if($idx_sep > 0) {
|
||||
$file->name = substr($f_entry, strrpos($f_entry, DIRECTORY_SEPARATOR) + 1);
|
||||
$file->path = $file->path . substr($f_entry, 0, strrpos($f_entry, DIRECTORY_SEPARATOR));
|
||||
} else {
|
||||
$file->name = $f_entry;
|
||||
}
|
||||
|
||||
$file->local_path = $entry["local-path"] . DIRECTORY_SEPARATOR . $f_entry;
|
||||
$file->type = $entry["type"];
|
||||
$file->hash = sha1_file($file->local_path);
|
||||
|
||||
if(strlen($file->hash) > 0) {
|
||||
foreach ($result as $e)
|
||||
if($e->hash == $file->hash) goto ignore;
|
||||
}
|
||||
array_push($result, $file);
|
||||
ignore:
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function fdump($name) {
|
||||
$file = fopen($name, "r") or die(json_encode([
|
||||
"success" => false,
|
||||
"error" => "missing file (" . $name . ")"
|
||||
]));
|
||||
|
||||
echo (fread($file, filesize($name)));
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
function handle_web_request() {
|
||||
if($_GET["type"] === "files") {
|
||||
header("Content-Type: text/plain");
|
||||
header("info-version: 1");
|
||||
|
||||
echo ("type\thash\tpath\tname\n");
|
||||
foreach (find_files(0b10) as $file) {
|
||||
echo $file->type . "\t" . $file->hash . "\t" . $file->path . "\t" . $file->name . "\n";
|
||||
}
|
||||
echo "html\t".sha1("main")."\t.\tindex.html\n";
|
||||
die;
|
||||
} else if($_GET["type"] === "file") {
|
||||
header("Content-Type: text/plain");
|
||||
|
||||
$available_files = find_files(0b10);
|
||||
foreach ($available_files as $entry) {
|
||||
if(($entry->path == $_GET["path"]) && ($entry->name == $_GET["name"])) {
|
||||
fdump($entry->local_path);
|
||||
die();
|
||||
}
|
||||
}
|
||||
if($_GET["name"] == "index.html") {
|
||||
$CLIENT = true;
|
||||
include "./index.php";
|
||||
die();
|
||||
}
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"error" => "missing file!"
|
||||
]));
|
||||
} else die(json_encode([
|
||||
"success" => false,
|
||||
"error" => "invalid action!"
|
||||
]));
|
||||
}
|
||||
|
||||
if(isset($_SERVER['REQUEST_METHOD'])) {
|
||||
handle_web_request();
|
||||
die(); //Should never happen!
|
||||
}
|
||||
|
||||
if(isset($_SERVER["argv"])) { //Executed by command line
|
||||
if(strpos(PHP_OS, "Linux") == -1) {
|
||||
error_log("Invalid operating system! Help tool only available under linux!");
|
||||
exit(1);
|
||||
}
|
||||
if(count($_SERVER["argv"]) < 2) {
|
||||
error_log("Invalid parameters!");
|
||||
goto help;
|
||||
}
|
||||
if($_SERVER["argv"][1] == "help") {
|
||||
help:
|
||||
echo "php " . $_SERVER["argv"][0] . " <type> <args...>" . PHP_EOL;
|
||||
echo " generate <web/client>" . PHP_EOL;
|
||||
echo " list <web/client>" . PHP_EOL;
|
||||
exit(1);
|
||||
} else if($_SERVER["argv"][1] == "list") {
|
||||
if(count($_SERVER["argv"]) < 3) {
|
||||
error_log("Invalid parameter count!");
|
||||
goto help;
|
||||
}
|
||||
|
||||
echo ("type\thash\tpath\tname\n");
|
||||
foreach (find_files(0b10) as $file) {
|
||||
echo $file->type . "\t" . $file->hash . "\t" . $file->path . "\t" . $file->name . "\n";
|
||||
}
|
||||
echo "html\t".sha1("main")."\t.\tindex.html\n";
|
||||
return;
|
||||
} else if($_SERVER["argv"][1] == "generate") {
|
||||
$state = 0;
|
||||
|
||||
$flagset = 0b00;
|
||||
$environment = "";
|
||||
if($_SERVER["argv"][2] == "web") {
|
||||
$flagset = 0b01;
|
||||
$environment = "web/environment";
|
||||
} else if($_SERVER["argv"][2] == "client") {
|
||||
$flagset = 0b10;
|
||||
$environment = "client/environment";
|
||||
} else {
|
||||
error_log("Invalid type!");
|
||||
goto help;
|
||||
}
|
||||
|
||||
{
|
||||
exec($command = "rm -r " . $environment, $output, $state);
|
||||
exec($command = "mkdir " . $environment, $output, $state); if($state) goto handle_error;
|
||||
|
||||
$files = find_files(0b01);
|
||||
if(!chdir($environment)) {
|
||||
error_log("Failed to enter directory " . $environment . "!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
foreach($files as $file) {
|
||||
if(!is_dir($file->path)) {
|
||||
exec($command = "mkdir -p " . $file->path, $output, $state);
|
||||
if($state) goto handle_error;
|
||||
}
|
||||
|
||||
$parent = substr_count(realpath($file->path), DIRECTORY_SEPARATOR) - substr_count(realpath('.'), DIRECTORY_SEPARATOR);
|
||||
|
||||
$path = "../../";
|
||||
for($index = 0; $index < $parent; $index++)
|
||||
$path = $path . "../";
|
||||
exec($command = "ln -s " . $path . $file->local_path . " " . $file->path, $output, $state);
|
||||
if($state) goto handle_error;
|
||||
echo $command . PHP_EOL;
|
||||
}
|
||||
echo "Generated!" . PHP_EOL;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
handle_error:
|
||||
error_log("Failed to execute command '" . $command . "'!");
|
||||
error_log("Command returned code " . $state . ". Output: " . PHP_EOL);
|
||||
foreach ($output as $line)
|
||||
error_log($line);
|
||||
exit(1);
|
||||
}
|
||||
}
|
172
index.php
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
$testXF = false;
|
||||
|
||||
|
||||
$_INCLIDE_ONLY = true;
|
||||
if (file_exists('auth.php'))
|
||||
include_once('auth.php');
|
||||
else if (file_exists('auth/auth.php'))
|
||||
|
@ -29,11 +29,15 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>TeaSpeak-Web</title>
|
||||
|
||||
<?php
|
||||
if(!isset($CLIENT) || !$CLIENT) {
|
||||
if($CLIENT) {
|
||||
echo "<title>TeaClient</title>";
|
||||
} else {
|
||||
echo "<title>TeaSpeak-Web</title>";
|
||||
}
|
||||
?>
|
||||
|
||||
<link rel="stylesheet" href="css/main.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/scroll.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/ts/tab.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/ts/chat.css" type="text/css">
|
||||
|
@ -47,7 +51,6 @@
|
|||
<link rel="stylesheet" href="css/control_bar.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/context_menu.css" type="text/css">
|
||||
<link rel="stylesheet" href="vendor/bbcode/xbbcode.css" type="text/css">
|
||||
<?php } ?>
|
||||
<!-- https://localhost:9987/?forward_url=http%3A%2F%2Flocalhost%3A63344%2FWeb-Client%2Findex.php%3F_ijt%3D82b1uhmnh0a5l1n35nnjps5eid%26loader_ignore_age%3D1%26connect_default_host%3Dlocalhost%26default_connect_type%3Dforum%26default_connect_url%3Dtrue%26default_connect_type%3Dteamspeak%26default_connect_url%3Dlocalhost%253A9987 -->
|
||||
<!-- PHP generated properies -->
|
||||
<!-- localhost:63342/TeaSpeak-Web/index.php?_ijt=o48hmliefjoa8cer8v7mpl98pj&connect_default_host=192.168.43.141 -->
|
||||
|
@ -86,8 +89,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<!-- No javascript error -->
|
||||
<div style="display: block; position: fixed; top: 0px; bottom: 0px; left: 0px; right: 0px; background-color: gray; z-index: 1000; text-align: center;"
|
||||
class="no-js">
|
||||
<div style="display: block; position: fixed; top: 0px; bottom: 0px; left: 0px; right: 0px; background-color: gray; z-index: 1000; text-align: center;" class="no-js">
|
||||
<div style="position: relative; display: inline-block; top: 30%">
|
||||
<img src="img/script.svg" height="128px">
|
||||
<h1>Please enable JavaScript</h1>
|
||||
|
@ -96,7 +98,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" class="no-js">
|
||||
var elements = document.getElementsByClassName("no-js");
|
||||
let elements = document.getElementsByClassName("no-js");
|
||||
while (elements.length > 0) //Removing these elements (even self)
|
||||
elements.item(0).remove();
|
||||
</script>
|
||||
|
@ -119,7 +121,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Critical load error -->
|
||||
<div style="display: none; position: fixed; top: 0px; bottom: 0px; left: 0px; right: 0px; background-color: gray; z-index: 1000; text-align: center;"
|
||||
<div style="display: none; position: fixed; top: 0; bottom: 0; left: 0; right: 0; background-color: gray; z-index: 1000; text-align: center;"
|
||||
id="critical-load">
|
||||
<div style="position: relative; display: inline-block; top: 30%">
|
||||
<img src="img/script.svg" height="128px">
|
||||
|
@ -128,157 +130,10 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- -->
|
||||
TeaSpeak-Web<br>
|
||||
|
||||
<div style="width: 100%; display: flex; justify-content: center">
|
||||
<div style="width: 1200px; height: 900px; display: flex; flex-direction: column; resize: both; margin: 20px">
|
||||
<!-- Container -->
|
||||
<div style="height: 45px; width: 100%; border-radius: 2px 0px 0px 0px; border-bottom-width: 0px; background-color: lightgrey"
|
||||
class="main_container">
|
||||
<div id="control_bar" class="control_bar">
|
||||
<div class="button btn_connect" title="Connect to a server">
|
||||
<div class="icon_x32 client-connect"></div>
|
||||
</div>
|
||||
<div class="button btn_disconnect" title="Disconnect from server" style="display: none">
|
||||
<div class="icon_x32 client-disconnect"></div>
|
||||
</div>
|
||||
<!--<div class="button btn_disconnect"><div class="icon_x32 client-disconnect"></div></div>-->
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="button-dropdown btn_away" title="Toggle away status">
|
||||
<div class="buttons">
|
||||
<div class="button icon_x32 client-away btn_away_toggle"></div>
|
||||
<div class="button-dropdown">
|
||||
<div class="arrow"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<div class="btn_away_toggle"><div class="icon client-away"></div><a>Toggle away status</a></div>vendor/jquery/jquery.min.js
|
||||
<div class="btn_away_message"><div class="icon client-away"></div><a>Set away message</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button btn_mute_input">
|
||||
<div class="icon_x32 client-input_muted" title="Mute/unmute microphone"></div>
|
||||
</div>
|
||||
<div class="button btn_mute_output">
|
||||
<div class="icon_x32 client-output_muted" title="Mute/unmute headphones"></div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="button-dropdown btn_token" title="Use token">
|
||||
<div class="buttons">
|
||||
<div class="button icon_x32 client-token btn_token_use"></div>
|
||||
<div class="button-dropdown">
|
||||
<div class="arrow"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<div class="btn_token_list"><div class="icon client-token"></div><a>List tokens</a></div>
|
||||
<div class="btn_token_use"><div class="icon client-token_use"></div><a>Use token</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="width: 100%"></div>
|
||||
<div class="button btn_permissions" title="View/edit permissions">
|
||||
<div class="icon_x32 client-permission_overview"></div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="button btn_open_settings" title="Edit global client settings">
|
||||
<div class="icon_x32 client-settings"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="flex-direction: row; height: 100%; width: 100%; display: flex">
|
||||
<div style="width: 60%; flex-direction: column;">
|
||||
<div style="height: 60%; border-radius: 0px 0px 0px 0px; border-right-width: 0px; overflow: auto; overflow-x: visible"
|
||||
class="main_container">
|
||||
<div class="channelTree" id="channelTree"></div>
|
||||
</div> <!-- Channel tree -->
|
||||
<div style="height: 40%; border-radius: 0px 0px 0px 2px; border-top-width: 0px; border-right-width: 0px;"
|
||||
class="main_container">
|
||||
<div id="chat">
|
||||
<div class="messages">
|
||||
<div class="message_box"></div>
|
||||
</div>
|
||||
<div class="chats"></div>
|
||||
<div class="input">
|
||||
<!--<div contentEditable="true" class="input_box"></div>-->
|
||||
<textarea class="input_box" title=""></textarea>
|
||||
<button>Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- Chat window -->
|
||||
</div>
|
||||
<div style="width: 40%; border-radius: 0px 0px 2px 0px;" class="main_container">
|
||||
<div id="select_info" class="select_info" style="width: 100%; max-width: 100%">
|
||||
</div>
|
||||
</div> <!-- Selection info -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contextMenu" class="context-menu"></div>
|
||||
|
||||
<!--
|
||||
<div style="background-color:white;">
|
||||
<div style=" color: white; mix-blend-mode: difference;">And stay alive... XXXXXXX</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div id="templates"></div>
|
||||
<div id="music-test"></div>
|
||||
<div style="height: 100px"></div>
|
||||
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* @param path {string}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function load_script0(path) {
|
||||
if(window.require !== undefined) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log("load via node %o %o", resolve, reject);
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
ipcRenderer.send("load-file", "loaded-file-" + path, path);
|
||||
ipcRenderer.once("loaded-file-" + path, (event, data) => {
|
||||
if(data === '404') {
|
||||
reject('script missing');
|
||||
return;
|
||||
}
|
||||
const tag = document.createElement("script");
|
||||
tag.type = "text\/javascript";
|
||||
document.getElementById("scripts").appendChild(tag);
|
||||
tag.src = URL.createObjectURL(new Blob([decodeURIComponent(data)], {type: "text/javascript"}));
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} else { //Normal web browser load
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log("load via web %o %o", resolve, reject);
|
||||
const tag = document.createElement("script");
|
||||
tag.type = "text\/javascript";
|
||||
tag.onerror = error => {
|
||||
console.log(error);
|
||||
tag.remove();
|
||||
reject(error);
|
||||
};
|
||||
tag.onload = () => resolve();
|
||||
document.getElementById("scripts").appendChild(tag);
|
||||
tag.src = path;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(window.require !== undefined) {
|
||||
module.paths.push("/home/wolverindev/Development-WebApp/")
|
||||
window.$ = require("jquery");
|
||||
} else {
|
||||
load_script0("vendor/jquery/jquery.min.js");
|
||||
}
|
||||
load_script0("load.js");
|
||||
</script>
|
||||
<div id="templates"></div>
|
||||
</body>
|
||||
<?php if(!$CLIENT) { ?>
|
||||
<footer>
|
||||
<div class="container" style="display: flex; flex-direction: row; align-content: space-between;">
|
||||
<div style="align-self: center; position: fixed; left: 5px;">Open source on <a href="https://github.com/TeaSpeak/TeaSpeak-Web" style="display: inline-block; position: relative">github.com</a></div>
|
||||
|
@ -296,4 +151,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<?php } ?>
|
||||
</html>
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
$APP_FILE_LIST = [ ];
|
||||
$APP_FILE_ROOT = [
|
||||
"js" => "../js/",
|
||||
"css" => "../css/",
|
||||
"vendor" => "../"
|
||||
];
|
||||
|
||||
function list_dir($base_dir, $match = null, &$results = array(), $dir = "") {
|
||||
$files = scandir($base_dir . $dir);
|
||||
|
||||
foreach($files as $key => $value){
|
||||
$path = $base_dir.$dir.DIRECTORY_SEPARATOR.$value;
|
||||
if(!is_dir($path)) {
|
||||
if(!$match || preg_match($match, $path))
|
||||
$results[] = ($dir ? $dir.DIRECTORY_SEPARATOR : "").$value;
|
||||
} else if($value != "." && $value != "..") {
|
||||
list_dir($base_dir, $match, $results, ($dir ? $dir.DIRECTORY_SEPARATOR : "").$value);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
function files_css() {
|
||||
global $APP_FILE_ROOT;
|
||||
return list_dir($APP_FILE_ROOT["css"], "/.*\.css$/");
|
||||
}
|
||||
|
||||
function files_js() {
|
||||
global $APP_FILE_ROOT;
|
||||
return list_dir($APP_FILE_ROOT["js"], "/.*\.js$/");
|
||||
}
|
||||
|
||||
function vendor() {
|
||||
return ["vendor/jquery/jquery.min.js", "vendor/bbcode/xbbcode.js", "vendor/jsrender/jsrender.min.js", "asm/generated/TeaWeb-Identity.js"];
|
||||
}
|
||||
|
||||
function fdump($name) {
|
||||
$file = fopen($name, "r") or die(json_encode([
|
||||
"success" => false,
|
||||
"error" => "missing file (" . $name . ")"
|
||||
]));
|
||||
|
||||
echo (fread($file, filesize($name)));
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
error_log("XXX: ");
|
||||
if($_GET["type"] === "files") {
|
||||
header("Content-Type: text/plain");
|
||||
header("info-version: 1");
|
||||
foreach(files_js() as $file) {
|
||||
echo $file . "\t" . sha1_file($APP_FILE_ROOT["js"] . DIRECTORY_SEPARATOR . $file) . "\n";
|
||||
}
|
||||
foreach(files_css() as $file) {
|
||||
echo $file . "\t" . sha1_file($APP_FILE_ROOT["css"] . DIRECTORY_SEPARATOR . $file) . "\n";
|
||||
}
|
||||
foreach(vendor() as $file) {
|
||||
echo $file . "\t" . sha1_file($APP_FILE_ROOT["vendor"] . DIRECTORY_SEPARATOR . $file) . "\n";
|
||||
}
|
||||
echo "main\t".sha1("main");
|
||||
die;
|
||||
} else if($_GET["type"] === "file") {
|
||||
error_log("XXX: " . $_GET["name"]);
|
||||
if($_GET["name"] == "main") {
|
||||
global $CLIENT;
|
||||
$CLIENT = true;
|
||||
include("../index.php");
|
||||
} else {
|
||||
foreach(files_css() as $file) {
|
||||
if($file == $_GET["name"]) {
|
||||
fdump($APP_FILE_ROOT["css"] . $_GET["name"]); //FIXME test path!
|
||||
die();
|
||||
}
|
||||
}
|
||||
foreach(files_js() as $file) {
|
||||
if($file == $_GET["name"]) {
|
||||
fdump($APP_FILE_ROOT["js"] . $_GET["name"]); //FIXME test path!
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
fdump('../' . $_GET["name"]); //FIXME remove this!
|
||||
}
|
||||
die();
|
||||
} else die(json_encode([
|
||||
"success" => false,
|
||||
"error" => "invalid action!"
|
||||
]));
|
30
package.json
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"name": "client",
|
||||
"version": "1.0.0",
|
||||
"description": "Welcome here! This repository is created with two reasons:\n 1. People can bring their own ideas and follow their implementation\n 2. People can see TeaSpeak Web client progress and avoid creating repetitive issues all the time.",
|
||||
"main": "main.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^9.4.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/emscripten": "0.0.31",
|
||||
"@types/jquery": "^3.3.0",
|
||||
"@types/websocket": "0.0.38"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/TeaSpeak/TeaWeb/TeaWeb.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/TeaSpeak/TeaWeb/issues"
|
||||
},
|
||||
"homepage": "https://github.com/TeaSpeak/TeaWeb/TeaWeb#readme"
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
.context-menu {
|
||||
overflow: visible;
|
||||
display: none;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
border: 1px solid #CCC;
|
||||
white-space: nowrap;
|
||||
font-family: sans-serif;
|
||||
background: #FFF;
|
||||
color: #333;
|
||||
padding: 3px; }
|
||||
.context-menu * {
|
||||
font-family: Arial, serif;
|
||||
font-size: 12px;
|
||||
white-space: pre;
|
||||
line-height: 1;
|
||||
vertical-align: middle; }
|
||||
.context-menu .entry {
|
||||
/*padding: 8px 12px;*/
|
||||
padding-right: 12px;
|
||||
cursor: pointer;
|
||||
list-style-type: none;
|
||||
transition: all .3s ease;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
display: flex; }
|
||||
.context-menu .entry.disabled {
|
||||
background-color: lightgray;
|
||||
cursor: not-allowed; }
|
||||
.context-menu .entry:hover:not(.disabled) {
|
||||
background-color: #DEF; }
|
||||
.context-menu .icon_empty, .context-menu .icon {
|
||||
margin-right: 4px; }
|
||||
.context-menu .arrow {
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
padding: 0;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
position: absolute;
|
||||
right: 3px; }
|
||||
.context-menu .sub-container {
|
||||
padding-right: 3px;
|
||||
position: relative; }
|
||||
.context-menu .sub-container:hover .sub-menu {
|
||||
display: block; }
|
||||
.context-menu .sub-menu {
|
||||
display: none;
|
||||
left: 100%;
|
||||
top: -4px;
|
||||
position: absolute;
|
||||
margin-left: 3px; }
|
||||
.context-menu .checkbox {
|
||||
margin-top: 1px;
|
||||
margin-left: 1px;
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 14px;
|
||||
margin-bottom: 12px;
|
||||
cursor: pointer;
|
||||
font-size: 22px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* Hide the browser's default checkbox */ }
|
||||
.context-menu .checkbox input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
display: none; }
|
||||
.context-menu .checkbox .checkmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 11px;
|
||||
width: 11px;
|
||||
background-color: #eee; }
|
||||
.context-menu .checkbox .checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 4px;
|
||||
top: 1px;
|
||||
width: 3px;
|
||||
height: 7px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg); }
|
||||
.context-menu .checkbox:hover input ~ .checkmark {
|
||||
background-color: #ccc; }
|
||||
.context-menu .checkbox input:checked ~ .checkmark {
|
||||
background-color: #2196F3; }
|
||||
.context-menu .checkbox input:checked ~ .checkmark:after {
|
||||
display: block; }
|
||||
|
||||
/*# sourceMappingURL=context_menu.css.map */
|
|
@ -0,0 +1,93 @@
|
|||
.control_bar {
|
||||
display: flex;
|
||||
flex-direction: row; }
|
||||
.control_bar .divider {
|
||||
border-left: 2px solid gray;
|
||||
height: auto;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px; }
|
||||
.control_bar .button {
|
||||
cursor: pointer;
|
||||
background-color: lightgray;
|
||||
border-radius: 5px;
|
||||
align-items: center;
|
||||
border: 2px solid transparent;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px; }
|
||||
.control_bar .button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
border-color: rgba(255, 255, 255, 0.75);
|
||||
/*box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);*/ }
|
||||
.control_bar .button.activated {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
border-color: rgba(255, 255, 255, 0.75); }
|
||||
.control_bar .button.activated:hover {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
border-color: rgba(255, 255, 255, 0.75); }
|
||||
.control_bar .button-dropdown .buttons {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-template-rows: 100%;
|
||||
grid-gap: 2px; }
|
||||
.control_bar .button-dropdown .buttons .button {
|
||||
margin-right: 0px; }
|
||||
.control_bar .button-dropdown .buttons .button-dropdown {
|
||||
display: inline-flex;
|
||||
justify-content: space-around;
|
||||
width: 18px;
|
||||
cursor: pointer;
|
||||
border-radius: 0 5px 5px 0;
|
||||
align-items: center;
|
||||
border: 2px solid transparent;
|
||||
border-left: 0; }
|
||||
.control_bar .button-dropdown .buttons .button-dropdown .arrow {
|
||||
border: solid black;
|
||||
border-width: 0 3px 3px 0;
|
||||
display: inline-block;
|
||||
padding: 3px;
|
||||
transform: rotate(45deg);
|
||||
-webkit-transform: rotate(45deg);
|
||||
vertical-align: text-bottom; }
|
||||
.control_bar .button-dropdown .buttons:hover .button {
|
||||
border-right: 1px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
border-color: rgba(255, 255, 255, 0.75);
|
||||
/*box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);*/ }
|
||||
.control_bar .button-dropdown .buttons:hover .button-dropdown {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
border-color: rgba(255, 255, 255, 0.75);
|
||||
/*box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);*/ }
|
||||
.control_bar .button-dropdown .dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
margin-left: 5px;
|
||||
background-color: lightgray;
|
||||
border-radius: 5px;
|
||||
align-items: center;
|
||||
border: 2px solid transparent;
|
||||
border-color: rgba(255, 255, 255, 0.75);
|
||||
width: 230px;
|
||||
user-select: none;
|
||||
z-index: 1000;
|
||||
/*box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);*/ }
|
||||
.control_bar .button-dropdown .dropdown .icon {
|
||||
vertical-align: middle;
|
||||
margin-right: 5px; }
|
||||
.control_bar .button-dropdown .dropdown > div {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
padding: 1px 2px 1px 4px; }
|
||||
.control_bar .button-dropdown .dropdown > div:hover {
|
||||
background-color: rgba(0, 0, 0, 0.25); }
|
||||
.control_bar .button-dropdown .dropdown > div:first-of-type {
|
||||
border-radius: 2px 2px 0 0; }
|
||||
.control_bar .button-dropdown .dropdown > div:last-of-type {
|
||||
border-radius: 0 0 2px 2px; }
|
||||
.control_bar .button-dropdown:hover .dropdown.displayed {
|
||||
display: block; }
|
||||
|
||||
/*# sourceMappingURL=control_bar.css.map */
|
Before Width: | Height: | Size: 334 KiB After Width: | Height: | Size: 334 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 669 B After Width: | Height: | Size: 669 B |
Before Width: | Height: | Size: 574 B After Width: | Height: | Size: 574 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 653 B After Width: | Height: | Size: 653 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
@ -36,13 +36,13 @@ namespace app {
|
|||
};
|
||||
|
||||
Module['onAbort'] = message => {
|
||||
//Module['onAbort'] = undefined;
|
||||
//displayCriticalError("Could not load webassembly files!<br>Message: <code>" + message + "</code>", false);
|
||||
Module['onRuntimeInitialized'](); //FIXME! Just a bridge
|
||||
Module['onAbort'] = undefined;
|
||||
displayCriticalError("Could not load webassembly files!<br>Message: <code>" + message + "</code>", false);
|
||||
};
|
||||
|
||||
Module['locateFile'] = file => {
|
||||
console.log(file + "|" + type);
|
||||
console.log("File path for " + file);
|
||||
return "wasm/" + file;
|
||||
switch (type) {
|
||||
case Type.RELEASE:
|
||||
return "js/assembly/" + file;
|
||||
|
@ -59,22 +59,6 @@ namespace app {
|
|||
}
|
||||
}
|
||||
|
||||
/* safari remove "fix" */
|
||||
Object.defineProperty(Element.prototype, "remove", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
value: function(){
|
||||
this.parentElement.removeChild(this);
|
||||
}
|
||||
});
|
||||
|
||||
if(typeof Module === "undefined")
|
||||
this["Module"] = {};
|
||||
app.initialize();
|
||||
app.loadedListener.push(fadeoutLoader);
|
||||
|
||||
|
||||
function loadScripts(paths: (string | string[])[]) : {path: string, promise: Promise<Boolean>}[] {
|
||||
let result = [];
|
||||
for(let path of paths)
|
||||
|
@ -94,7 +78,18 @@ function loadScript(path: string | string[]) : Promise<Boolean> {
|
|||
});
|
||||
});
|
||||
} else {
|
||||
return load_script0(path);
|
||||
return new Promise((resolve, reject) => {
|
||||
const tag = document.createElement("script");
|
||||
tag.type = "application/javascript";
|
||||
tag.onerror = error => {
|
||||
console.log(error);
|
||||
tag.remove();
|
||||
reject(error);
|
||||
};
|
||||
tag.onload = () => resolve();
|
||||
document.getElementById("scripts").appendChild(tag);
|
||||
tag.src = path;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,33 +200,21 @@ function awaitLoad(promises: {path: string, promise: Promise<Boolean>}[]) : Prom
|
|||
if(error instanceof TypeError) {
|
||||
console.error(error);
|
||||
let name = (error as any).fileName + "@" + (error as any).lineNumber + ":" + (error as any).columnNumber;
|
||||
displayCriticalError("Failed to execute script <code>" + name + "</code>.<hr>If you believe that it isn't you're mistake<br>then please contact an administrator!", false);
|
||||
displayCriticalError("Failed to execute script <code>" + name + "</code>.<hr>If you believe that it isn't your mistake<br>then please contact an administrator!");
|
||||
return;
|
||||
} else {
|
||||
console.error("Failed to load script " + entry.path);
|
||||
}
|
||||
displayCriticalError("Failed to load script " + formatPath(entry.path) + ".<hr>If you believe that it isn't you're mistake<br>then please contact an administrator!", false);
|
||||
displayCriticalError("Failed to load script " + formatPath(entry.path) + ".<hr>If you believe that it isn't your mistake<br>then please contact an administrator!");
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayCriticalError(message: string, closeable: boolean = true) {
|
||||
if(typeof(createErrorModal) !== 'undefined') {
|
||||
createErrorModal("A critical error occurred while loading the page!", message, {closeable: closeable}).open();
|
||||
} else {
|
||||
let tag = document.getElementById("critical-load");
|
||||
let detail = tag.getElementsByClassName("detail")[0];
|
||||
detail.innerHTML = message;
|
||||
|
||||
tag.style.display = "block";
|
||||
}
|
||||
fadeoutLoader();
|
||||
}
|
||||
|
||||
function loadTemplates() {
|
||||
//Load the templates
|
||||
$.ajax("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/templates.html", {
|
||||
$.ajax("templates.html", {
|
||||
cache: false, //Change this when in release mode
|
||||
}).then((element, status) => {
|
||||
let node = document.createElement("html");
|
||||
|
@ -243,35 +226,26 @@ function loadTemplates() {
|
|||
tags = node.children;
|
||||
|
||||
let root = document.getElementById("templates");
|
||||
while(tags.length > 0)
|
||||
root.appendChild(tags.item(0));
|
||||
while(tags.length > 0){
|
||||
let tag = tags.item(0);
|
||||
if(tag.id == "tmpl_main")
|
||||
document.getElementsByTagName("body").item(0).appendChild(tag);
|
||||
else
|
||||
root.appendChild(tag);
|
||||
|
||||
}
|
||||
/*
|
||||
root = document.getElementById("script");
|
||||
while(tags.length > 0)
|
||||
root.appendChild(tags.item(0));
|
||||
*/
|
||||
}).catch(error => {
|
||||
console.error("Could not load templates!");
|
||||
console.log(error);
|
||||
displayCriticalError("Could not load HTML templates!", false);
|
||||
displayCriticalError("Could not load HTML templates!");
|
||||
});
|
||||
}
|
||||
|
||||
navigator.browserSpecs = (function(){
|
||||
let ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
|
||||
if(/trident/i.test(M[1])){
|
||||
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
|
||||
return {name:'IE',version:(tem[1] || '')};
|
||||
}
|
||||
if(M[1]=== 'Chrome'){
|
||||
tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
|
||||
if(tem != null) return {name:tem[1].replace('OPR', 'Opera'),version:tem[2]};
|
||||
}
|
||||
M = M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
|
||||
if((tem = ua.match(/version\/(\d+)/i))!= null)
|
||||
M.splice(1, 1, tem[1]);
|
||||
return {name:M[0], version:M[1]};
|
||||
})();
|
||||
|
||||
console.log(navigator.browserSpecs); //Object { name: "Firefox", version: "42" }
|
||||
//TODO release config!
|
||||
function loadSide() {
|
||||
if(typeof (WebAssembly) === "undefined" || typeof (WebAssembly.compile) === "undefined") {
|
||||
|
@ -288,12 +262,18 @@ function loadSide() {
|
|||
displayCriticalError("You require WebAssembly for TeaSpeak-Web!");
|
||||
return;
|
||||
}
|
||||
if(window.require !== undefined) {
|
||||
const app = require('electron').remote.app;
|
||||
module.paths.push(app.getAppPath());
|
||||
window.$ = require("jquery");
|
||||
}
|
||||
//Load the general scripts and required scripts
|
||||
awaitLoad(loadScripts([
|
||||
//["vendor/jquery/jquery.min.js", /*"https://code.jquery.com/jquery-latest.min.js"*/],
|
||||
["vendor/jquery/jquery.min.js", /*"https://code.jquery.com/jquery-latest.min.js"*/],
|
||||
["vendor/bbcode/xbbcode.js"],
|
||||
["https://webrtc.github.io/adapter/adapter-latest.js"]
|
||||
])).then(() => awaitLoad(loadScripts([
|
||||
])).then(() => {
|
||||
}).then(() => awaitLoad(loadScripts([
|
||||
//["https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"]
|
||||
["vendor/jsrender/jsrender.min.js"]
|
||||
]))).then(() => {
|
||||
|
@ -304,8 +284,6 @@ function loadSide() {
|
|||
});
|
||||
}
|
||||
|
||||
loadSide();
|
||||
|
||||
//FUN: loader_ignore_age=0&loader_default_duration=1500&loader_default_age=5000
|
||||
function fadeoutLoader(duration = undefined, minAge = undefined, ignoreAge = undefined) {
|
||||
let settingsDefined = typeof(StaticSettings) !== "undefined";
|
||||
|
@ -334,4 +312,54 @@ function fadeoutLoader(duration = undefined, minAge = undefined, ignoreAge = und
|
|||
$(".loader .half").animate({width: 0}, duration, () => {
|
||||
$(".loader").detach();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* safari remove "fix" */
|
||||
if(Element.prototype.remove === undefined)
|
||||
Object.defineProperty(Element.prototype, "remove", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
value: function(){
|
||||
this.parentElement.removeChild(this);
|
||||
}
|
||||
});
|
||||
|
||||
if(typeof Module === "undefined")
|
||||
this["Module"] = {};
|
||||
app.initialize();
|
||||
app.loadedListener.push(fadeoutLoader);
|
||||
|
||||
if(!window.displayCriticalError) {
|
||||
window.displayCriticalError = function(message: string) {
|
||||
if(typeof(createErrorModal) !== 'undefined') {
|
||||
createErrorModal("A critical error occurred while loading the page!", message, {closeable: false}).open();
|
||||
} else {
|
||||
let tag = document.getElementById("critical-load");
|
||||
let detail = tag.getElementsByClassName("detail")[0];
|
||||
detail.innerHTML = message;
|
||||
|
||||
tag.style.display = "block";
|
||||
}
|
||||
fadeoutLoader();
|
||||
}
|
||||
}
|
||||
|
||||
navigator.browserSpecs = (function(){
|
||||
let ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
|
||||
if(/trident/i.test(M[1])){
|
||||
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
|
||||
return {name:'IE',version:(tem[1] || '')};
|
||||
}
|
||||
if(M[1]=== 'Chrome'){
|
||||
tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
|
||||
if(tem != null) return {name:tem[1].replace('OPR', 'Opera'),version:tem[2]};
|
||||
}
|
||||
M = M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
|
||||
if((tem = ua.match(/version\/(\d+)/i))!= null)
|
||||
M.splice(1, 1, tem[1]);
|
||||
return {name:M[0], version:M[1]};
|
||||
})();
|
||||
|
||||
console.log(navigator.browserSpecs); //Object { name: "Firefox", version: "42" }
|
||||
loadSide();
|
|
@ -16,9 +16,15 @@ let chat: ChatBox;
|
|||
|
||||
let forumIdentity: TeaForumIdentity;
|
||||
|
||||
const js_render = window.jsrender || $;
|
||||
|
||||
function main() {
|
||||
$.views.settings.allowCode(true);
|
||||
$.views.tags("rnd", (argument) => {
|
||||
if(!js_render) {
|
||||
displayCriticalError("Missing jsrender extension!");
|
||||
return;
|
||||
}
|
||||
js_render.views.settings.allowCode(true);
|
||||
js_render.views.tags("rnd", (argument) => {
|
||||
let min = parseInt(argument.substr(0, argument.indexOf('~')));
|
||||
let max = parseInt(argument.substr(argument.indexOf('~') + 1));
|
||||
|
|
@ -108,7 +108,14 @@ if(typeof ($) !== "undefined") {
|
|||
}
|
||||
if(!$.prototype.renderTag) {
|
||||
$.prototype.renderTag = function (values?: any) : JQuery {
|
||||
let result = $(this.render(values));
|
||||
let result;
|
||||
if(this.render) {
|
||||
result = $(this.render(values));
|
||||
} else {
|
||||
result = window.jsrender.templates("xxx", this.html())
|
||||
result = window.jsrender.render["xxx"](values);
|
||||
result = $(result);
|
||||
}
|
||||
result.find("node").each((index, element) => {
|
||||
$(element).replaceWith(values[$(element).attr("key")] || values[0][$(element).attr("key")]);
|
||||
});
|