move classes and base config into includes directory
This commit is contained in:
692
zefie_wtvp_minisrv/includes/WTVShared.js
Normal file
692
zefie_wtvp_minisrv/includes/WTVShared.js
Normal file
@@ -0,0 +1,692 @@
|
||||
/**
|
||||
* Shared functions across all classes and apps
|
||||
*/
|
||||
|
||||
|
||||
class WTVShared {
|
||||
|
||||
path = require('path');
|
||||
fs = require('fs');
|
||||
v8 = require('v8');
|
||||
zlib = require('zlib');
|
||||
CryptoJS = require('crypto-js');
|
||||
html_entities = require('html-entities'); // used externally by service scripts
|
||||
sanitizeHtml = require('sanitize-html');
|
||||
iconv = require('iconv-lite');
|
||||
parentDirectory = process.cwd()
|
||||
|
||||
minisrv_config = [];
|
||||
|
||||
constructor(minisrv_config, quiet = false) {
|
||||
if (minisrv_config == null) this.minisrv_config = this.readMiniSrvConfig(true, !quiet);
|
||||
else this.minisrv_config = minisrv_config;
|
||||
|
||||
if (!String.prototype.reverse) {
|
||||
String.prototype.reverse = function () {
|
||||
var splitString = this.split("");
|
||||
var reverseArray = splitString.reverse();
|
||||
var joinArray = reverseArray.join("");
|
||||
return joinArray;
|
||||
}
|
||||
}
|
||||
if (!String.prototype.hexEncode) {
|
||||
String.prototype.hexEncode = function () {
|
||||
var result = '';
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
result += this.charCodeAt(i).toString(16);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getServiceString(service, overrides = {}) {
|
||||
// used externally by service scripts
|
||||
if (service === "all") {
|
||||
var out = "";
|
||||
Object.keys(minisrv_config.services).forEach(function (k) {
|
||||
if (overrides.exceptions) {
|
||||
Object.keys(overrides.exceptions).forEach(function (j) {
|
||||
if (k != overrides.exceptions[j]) out += minisrv_config.services[k].toString(overrides) + "\n";
|
||||
});
|
||||
} else {
|
||||
out += minisrv_config.services[k].toString(overrides) + "\n";
|
||||
}
|
||||
});
|
||||
return out;
|
||||
} else {
|
||||
if (!minisrv_config.services[service]) {
|
||||
throw ("SERVICE ERROR: Attempted to provision unconfigured service: " + service)
|
||||
} else {
|
||||
return minisrv_config.services[service].toString(overrides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseBool(val) {
|
||||
if (typeof val === 'string')
|
||||
val = val.toLowerCase();
|
||||
|
||||
return val === true || val === "true" || val === 1;
|
||||
}
|
||||
|
||||
|
||||
getQueryString(query) {
|
||||
// for easy retrofitting old code to work with the webtvism of allowing multiple of the same query name
|
||||
// pass it the query, and it will return a string regardless. if its a string it just sends it back
|
||||
// if its an array we just pull the last object
|
||||
if (typeof (query) === "object")
|
||||
return query[(Object.keys(query).length - 1)];
|
||||
else
|
||||
return query
|
||||
}
|
||||
|
||||
htmlEntitize(string, process_newline = false) {
|
||||
string = this.html_entities.encode(string).replace(/'/g, "'");
|
||||
|
||||
if (process_newline) string = string.replace(/\n/gi, "<br>").replace(/\r/gi, "");
|
||||
return string;
|
||||
}
|
||||
|
||||
sanitizeSignature(string) {
|
||||
var allowedSchemes = ['http', 'https', 'ftp', 'mailto'];
|
||||
var self = this;
|
||||
Object.keys(this.minisrv_config.services).forEach(function (k) {
|
||||
var flags = self.minisrv_config.services[k].flags;
|
||||
if (flags) {
|
||||
if (flags == "0x00000004" || flags == "0x00000007") {
|
||||
allowedSchemes.push(self.minisrv_config.services[k].name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const clean = this.sanitizeHtml(string, {
|
||||
allowedTags: ['a', 'audioscope', 'b', 'bgsound', 'big', 'blackface', 'blockquote', 'bq', 'br', 'caption', 'center', 'cite', 'c', 'dd', 'dfn', 'div', 'dl', 'dt', 'fn', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'html', 'i', 'img', 'label', 'li', 'link', 'listing', 'em', 'marquee', 'nobr', 'note', 'ol', 'p', 'plaintext', 'pre', 's', 'samp', 'small', 'span', 'strike', 'strong', 'style', 'sub', 'sup', 'tbody', 'table', 'td', 'th', 'tr', 'tt', 'u', 'ul'],
|
||||
disallowedTagsMode: 'discard',
|
||||
allowedAttributes: {
|
||||
a: ['href', 'name', 'target'],
|
||||
img: ['src', 'alt', 'title', 'width', 'height', 'loading'],
|
||||
bgsound: ['src', 'loop'],
|
||||
font: ['size', 'name', 'color'],
|
||||
marquee: ['speed'],
|
||||
},
|
||||
allowedSchemes: allowedSchemes,
|
||||
allowedSchemesByTag: {},
|
||||
allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],
|
||||
allowVulnerableTags: true,
|
||||
allowProtocolRelative: false
|
||||
})
|
||||
// todo: add missing user open tags (eg </i> if user did not close it) (might be done by sanitize-html?)
|
||||
// todo: figure out bgcolor and text color voodoo
|
||||
return clean;
|
||||
}
|
||||
|
||||
|
||||
isASCII(str) {
|
||||
for (var i = 0, strLen = str.length; i < strLen; ++i) {
|
||||
if (str.charCodeAt(i) > 127) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
isHTML(str) {
|
||||
return /<[a-z][\s\S]*>/i.test(str);
|
||||
}
|
||||
|
||||
utf8Decode(utf8String) {
|
||||
if (typeof utf8String != 'string') throw new TypeError('parameter <20>utf8String<6E> is not a string');
|
||||
// note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char!
|
||||
const unicodeString = utf8String.replace(
|
||||
/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars
|
||||
function (c) { // (note parentheses for precedence)
|
||||
var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f);
|
||||
return String.fromCharCode(cc);
|
||||
}
|
||||
).replace(
|
||||
/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars
|
||||
function (c) { // (note parentheses for precedence)
|
||||
var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f;
|
||||
return String.fromCharCode(cc);
|
||||
}
|
||||
);
|
||||
return unicodeString;
|
||||
}
|
||||
|
||||
decodeBufferText(buf) {
|
||||
var out = "";
|
||||
out = this.utf8Decode(this.iconv.decode(Buffer.from(buf),'ISO-8859-1'));
|
||||
return out;
|
||||
}
|
||||
|
||||
returnAbsolutePath(check_path) {
|
||||
if (check_path.substring(0, 1) != this.path.sep && check_path.substring(1, 1) != ":") {
|
||||
// non-absolute path, so use current directory as base
|
||||
check_path = this.parentDirectory + this.path.sep + check_path;
|
||||
} else {
|
||||
// already absolute path
|
||||
}
|
||||
return check_path;
|
||||
}
|
||||
|
||||
isMiniBrowser(ssid_session) {
|
||||
return (ssid_session.get("wtv-need-upgrade") || ssid_session.get("wtv-used-8675309")) ? true : false;
|
||||
}
|
||||
|
||||
isOldBuild(ssid_session) {
|
||||
return (this.isMiniBrowser(ssid_session) || parseInt(ssid_session.get("wtv-system-version")) < 3500) ? true : false;
|
||||
}
|
||||
|
||||
getUserConfig() {
|
||||
try {
|
||||
var user_config_filename = this.getAbsolutePath("user_config.json", this.parentDirectory);
|
||||
if (this.fs.lstatSync(user_config_filename)) {
|
||||
try {
|
||||
var minisrv_user_config = JSON.parse(this.fs.readFileSync(user_config_filename));
|
||||
} catch (e) {
|
||||
console.error("ERROR: Could not read user_config.json", e);
|
||||
var throw_me = true;
|
||||
}
|
||||
} else {
|
||||
var minisrv_user_config = {}
|
||||
}
|
||||
return minisrv_user_config;
|
||||
} catch (e) {
|
||||
if (minisrv_config.config.debug_flags) {
|
||||
if (minisrv_config.config.debug_flags.debug) console.error(" * Notice: Could not find user configuration (user_config.json). Using default configuration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
readMiniSrvConfig(user_config = true, notices = true, reload_notice = false) {
|
||||
if (notices || reload_notice) console.log(" *** Reading global configuration...");
|
||||
try {
|
||||
var minisrv_config = JSON.parse(this.fs.readFileSync(this.getAbsolutePath("config.json", __dirname)));
|
||||
} catch (e) {
|
||||
throw ("ERROR: Could not read config.json", e);
|
||||
}
|
||||
|
||||
var integrateConfig = function(main, user) {
|
||||
Object.keys(user).forEach(function (k) {
|
||||
if (typeof (user[k]) == 'object' && user[k] != null) {
|
||||
// new entry
|
||||
if (!main[k]) main[k] = new Array();
|
||||
// go down the rabbit hole
|
||||
main[k] = integrateConfig(main[k], user[k]);
|
||||
} else {
|
||||
// update main config
|
||||
main[k] = user[k];
|
||||
}
|
||||
});
|
||||
return main;
|
||||
}
|
||||
|
||||
if (user_config) {
|
||||
try {
|
||||
if (notices || reload_notice) console.log(" *** Reading user configuration...");
|
||||
var minisrv_user_config = this.getUserConfig()
|
||||
if (!minisrv_user_config) throw "ERROR: Could not read user_config.json";
|
||||
try {
|
||||
minisrv_config = integrateConfig(minisrv_config, minisrv_user_config)
|
||||
} catch (e) {
|
||||
console.error("ERROR: Could not read user_config.json", e);
|
||||
}
|
||||
} catch (e) {
|
||||
if (minisrv_config.config.debug_flags) {
|
||||
if (minisrv_config.config.debug_flags.debug) console.error(" * Notice: Could not find user configuration (user_config.json). Using default configuration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defaults
|
||||
minisrv_config.config.debug_flags = [];
|
||||
minisrv_config.config.debug_flags.debug = false;
|
||||
minisrv_config.config.debug_flags.quiet = true; // will squash minisrv_config.config.debug_flags.debug even if its true
|
||||
minisrv_config.config.debug_flags.show_headers = false;
|
||||
|
||||
if (minisrv_config.config.verbosity) {
|
||||
switch (minisrv_config.config.verbosity) {
|
||||
case 0:
|
||||
minisrv_config.config.debug_flags.debug = false;
|
||||
minisrv_config.config.debug_flags.quiet = true;
|
||||
minisrv_config.config.debug_flags.show_headers = false;
|
||||
if (notices) console.log(" * Console Verbosity level 0 (quietest)")
|
||||
break;
|
||||
case 1:
|
||||
minisrv_config.config.debug_flags.debug = false;
|
||||
minisrv_config.config.debug_flags.quiet = true;
|
||||
minisrv_config.config.debug_flags.show_headers = true;
|
||||
if (notices) console.log(" * Console Verbosity level 1 (headers shown)")
|
||||
break;
|
||||
case 2:
|
||||
minisrv_config.config.debug_flags.debug = true;
|
||||
minisrv_config.config.debug_flags.quiet = true;
|
||||
minisrv_config.config.debug_flags.show_headers = false;
|
||||
if (notices) console.log(" * Console Verbosity level 2 (verbose without headers)")
|
||||
break;
|
||||
case 3:
|
||||
minisrv_config.config.debug_flags.debug = true;
|
||||
minisrv_config.config.debug_flags.quiet = true;
|
||||
minisrv_config.config.debug_flags.show_headers = true;
|
||||
if (notices) console.log(" * Console Verbosity level 3 (verbose with headers)")
|
||||
break;
|
||||
default:
|
||||
minisrv_config.config.debug_flags.debug = true;
|
||||
minisrv_config.config.debug_flags.quiet = false;
|
||||
minisrv_config.config.debug_flags.show_headers = true;
|
||||
if (notices) console.log(" * Console Verbosity level 4 (debug verbosity)")
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (notices || reload_notice) console.log(" *** Configuration successfully read.");
|
||||
this.minisrv_config = minisrv_config;
|
||||
return this.minisrv_config;
|
||||
}
|
||||
|
||||
writeToUserConfig(config) {
|
||||
if (config) {
|
||||
try {
|
||||
var minisrv_user_config = this.getUserConfig();
|
||||
|
||||
// write back
|
||||
try {
|
||||
var new_user_config = {};
|
||||
Object.assign(new_user_config, minisrv_user_config, config);
|
||||
if (this.minisrv_config.config.debug_flags.debug) console.log(" * Writing new user configuration...");
|
||||
this.fs.writeFileSync(this.getAbsolutePath("user_config.json", parentDirectory), JSON.stringify(new_user_config, null, "\t"));
|
||||
}
|
||||
catch (e) {
|
||||
if (this.minisrv_config.config.debug_flags) {
|
||||
if (this.minisrv_config.config.debug_flags.debug) console.error(" * WARNING: Could not update user config. Data may have been lost.", e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (this.minisrv_config.config.debug_flags) {
|
||||
if (this.minisrv_config.config.debug_flags.debug) console.error(" * Notice: Could not find user configuration (user_config.json). Using default configuration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getMiniSrvConfig() {
|
||||
return this.minisrv_config;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Last-Modified date in Unix Timestamp format
|
||||
* @param {string} file Path to a file
|
||||
*/
|
||||
getFileLastModified(file) {
|
||||
var stats = this.fs.lstatSync(file);
|
||||
if (stats) return new Date(stats.mtimeMs);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Last-Modified date in a RFC7231 compliant UTC Date String
|
||||
* @param {string} file Path to a file
|
||||
*/
|
||||
getFileLastModifiedUTCString(file) {
|
||||
return this.getFileLastModified(file).toUTCString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a RFC7231 compliant UTC Date String from the current time
|
||||
* @param {Number} offset Offset from current time (+/-)
|
||||
* @returns {string} A RFC7231 compliant UTC Date String from the current time
|
||||
*/
|
||||
getUTCTime(offset = 0) {
|
||||
return new Date((new Date).getTime() + offset).toUTCString();
|
||||
}
|
||||
|
||||
urlEncodeBytes = (buf) => {
|
||||
let encoded = ''
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
const charBuf = Buffer.from('00', 'hex')
|
||||
charBuf.writeUInt8(buf[i])
|
||||
const char = charBuf.toString()
|
||||
// if the character is safe, then just print it, otherwise encode
|
||||
if (isUrlSafe(char)) {
|
||||
encoded += char
|
||||
} else {
|
||||
encoded += `%${charBuf.toString('hex').toUpperCase()}`
|
||||
}
|
||||
}
|
||||
return encoded
|
||||
}
|
||||
|
||||
urlDecodeBytes = (encoded) => {
|
||||
let decoded = Buffer.from('')
|
||||
for (let i = 0; i < encoded.length; i++) {
|
||||
if (encoded[i] === '%') {
|
||||
const charBuf = Buffer.from(`${encoded[i + 1]}${encoded[i + 2]}`, 'hex')
|
||||
decoded = Buffer.concat([decoded, charBuf])
|
||||
i += 2
|
||||
} else {
|
||||
const charBuf = Buffer.from(encoded[i])
|
||||
decoded = Buffer.concat([decoded, charBuf])
|
||||
}
|
||||
|
||||
}
|
||||
return decoded
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a censored SSID
|
||||
* @param {string|Array} obj SSID String or Headers Object
|
||||
*/
|
||||
filterSSID(obj) {
|
||||
if (this.minisrv_config.config.hide_ssid_in_logs === true) {
|
||||
if (typeof (obj) == "string") {
|
||||
if (obj.substr(0, 8) == "MSTVSIMU") {
|
||||
return obj.substr(0, 10) + ('*').repeat(10) + obj.substr(20);
|
||||
} else if (obj.substr(0, 5) == "1SEGA") {
|
||||
return obj.substr(0, 6) + ('*').repeat(6) + obj.substr(13);
|
||||
} else {
|
||||
return obj.substr(0, 6) + ('*').repeat(9);
|
||||
}
|
||||
} else {
|
||||
var newobj = Object.assign({}, obj);
|
||||
if (obj.post_data) newobj.post_data = obj.post_data;
|
||||
if (newobj["wtv-client-serial-number"]) {
|
||||
var ssid = newobj["wtv-client-serial-number"];
|
||||
if (ssid.substr(0, 8) == "MSTVSIMU") {
|
||||
newobj["wtv-client-serial-number"] = ssid.substr(0, 10) + ('*').repeat(10) + ssid.substr(20);
|
||||
} else if (ssid.substr(0, 5) == "1SEGA") {
|
||||
newobj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(6) + ssid.substr(13);
|
||||
} else {
|
||||
newobj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(9);
|
||||
}
|
||||
}
|
||||
return newobj;
|
||||
}
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
filterRequestLog(obj) {
|
||||
if (this.minisrv_config.config.filter_passwords_in_logs === true) {
|
||||
if (obj.query) {
|
||||
var newobj = Object.assign({}, obj);
|
||||
if (obj.post_data) newobj.post_data = obj.post_data;
|
||||
Object.keys(newobj.query).forEach(function (k) {
|
||||
var key = k.toLowerCase();
|
||||
switch (true) {
|
||||
case /passw(or)?d/.test(key):
|
||||
case /^pass$/.test(key):
|
||||
newobj.query[key] = ('*').repeat(newobj.query[key].length);
|
||||
break;
|
||||
}
|
||||
});
|
||||
return newobj;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
decodePostData(obj) {
|
||||
if (obj.post_data) {
|
||||
if (this.minisrv_config.config.filter_passwords_in_logs === true) {
|
||||
// complex, to filter
|
||||
var post_obj = {};
|
||||
post_obj.query = [];
|
||||
try {
|
||||
var post_text = obj.post_data.toString(this.CryptoJS.enc.Utf8);
|
||||
if (post_text.length > 0) {
|
||||
post_text = post_text.split("&");
|
||||
for (let i = 0; i < post_text.length; i++) {
|
||||
var qraw_split = post_text[i].split("=");
|
||||
if (qraw_split.length == 2) {
|
||||
var k = qraw_split[0];
|
||||
post_obj.query[k] = unescape(post_text[i].split("=")[1].replace(/\+/g, "%20"));
|
||||
}
|
||||
}
|
||||
}
|
||||
var post_obj = this.filterRequestLog(post_obj);
|
||||
post_text = "";
|
||||
Object.keys(post_obj.query).forEach(function (k) {
|
||||
post_text += k + "=" + post_obj.query[k] + "&";
|
||||
});
|
||||
post_text = post_text.substring(0, post_text.length - 1);
|
||||
obj.post_data = post_text.hexEncode();
|
||||
} catch (e) {
|
||||
obj.post_data = obj.post_data.toString(this.CryptoJS.enc.Hex);
|
||||
}
|
||||
} else {
|
||||
// simple, no filter
|
||||
obj.post_data = obj.post_data.toString();
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
unloadModule(moduleName) {
|
||||
// for handling template classes
|
||||
var solvedName = require.resolve(moduleName),
|
||||
nodeModule = require.cache[solvedName];
|
||||
if (nodeModule) {
|
||||
for (var i = 0; i < nodeModule.children.length; i++) {
|
||||
var child = nodeModule.children[i];
|
||||
this.unloadModule(child.filename);
|
||||
}
|
||||
delete require.cache[solvedName];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an absolute path
|
||||
* @param {string} path
|
||||
* @param {string} directory Root directory
|
||||
*/
|
||||
getAbsolutePath(path, directory = null) {
|
||||
if (directory) {
|
||||
if (path.indexOf(directory) == -1) {
|
||||
directory = this.getAbsolutePath(directory);
|
||||
try {
|
||||
if (this.fs.lstatSync(directory).isDirectory()) directory = directory + this.path.sep;
|
||||
} catch (e) { }
|
||||
path = directory + path;
|
||||
}
|
||||
}
|
||||
return this.fixPathSlashes(this.path.resolve(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a percentage
|
||||
* @param {number} partialValue
|
||||
* @param {number} totalValue
|
||||
* @returns {number} percentage
|
||||
*/
|
||||
getPercentage = function (partialValue, totalValue) {
|
||||
return Math.floor((100 * partialValue) / totalValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the file ends with .gz, remove it
|
||||
* @param {string} path
|
||||
* @return {string} path without gz, or unmodified path if it isnt a gz
|
||||
*/
|
||||
getFilePath(path) {
|
||||
var path_split = path.split('/');
|
||||
path_split.pop();
|
||||
return path_split.join('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file extension from a path
|
||||
* @param {string} path
|
||||
* @returns {String} File Extension (without dot)
|
||||
*/
|
||||
getFileExt(path) {
|
||||
return path.reverse().split(".")[0].reverse();
|
||||
}
|
||||
|
||||
getLineFromFile(filename, line_no, callback) {
|
||||
var stream = this.fs.createReadStream(filename, {
|
||||
flags: 'r',
|
||||
encoding: 'utf-8',
|
||||
fd: null,
|
||||
bufferSize: 64 * 1024
|
||||
});
|
||||
|
||||
|
||||
var fileData = '';
|
||||
stream.on('data', function (data) {
|
||||
fileData += data;
|
||||
|
||||
// The next lines should be improved
|
||||
var lines = fileData.split("\n");
|
||||
|
||||
if (lines.length >= +line_no) {
|
||||
stream.destroy();
|
||||
callback(null, lines[+line_no]);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', function () {
|
||||
callback('Error', null);
|
||||
});
|
||||
|
||||
stream.on('end', function () {
|
||||
callback('File end reached without finding line', null);
|
||||
});
|
||||
}
|
||||
|
||||
doErrorPage(code, data = null, pc_mode = false) {
|
||||
var headers = null;
|
||||
switch (code) {
|
||||
case 404:
|
||||
if (data === null) data = "The service could not find the requested page.";
|
||||
if (pc_mode) headers = "404 Not Found\n";
|
||||
else headers = code + " " + data + "\n";
|
||||
headers += "Content-Type: text/html\n";
|
||||
break;
|
||||
case 400:
|
||||
case 500:
|
||||
if (data === null) data = this.minisrv_config.config.service_name + " ran into a technical problem.";
|
||||
if (pc_mode) headers = "500 Internal Server Error\n";
|
||||
else headers = code + " " + data + "\n";
|
||||
headers += "Content-Type: text/html\n";
|
||||
break;
|
||||
case 401:
|
||||
if (data === null) data = "Access Denied.";
|
||||
if (pc_mode) headers = "401 Access Denied\n";
|
||||
else headers = code + " " + data + "\n";
|
||||
headers += "Content-Type: text/html\n";
|
||||
break;
|
||||
default:
|
||||
headers = code + " " + data + "\n";
|
||||
headers += "Content-Type: text/html\n";
|
||||
break;
|
||||
}
|
||||
console.error(" * doErrorPage Called:", code, data);
|
||||
return new Array(headers, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips bad things from paths
|
||||
* @param {string} base Base path
|
||||
* @param {string} target Sub path
|
||||
*/
|
||||
makeSafePath(base, target = null, force_forward_slash = false) {
|
||||
if (target) {
|
||||
target.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, "");
|
||||
var targetPath = this.path.posix.normalize(target)
|
||||
var output = this.fixPathSlashes(base + this.path.sep + targetPath);
|
||||
} else {
|
||||
base.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, "");
|
||||
var targetPath = this.path.posix.normalize(base)
|
||||
var output = this.fixPathSlashes(targetPath);
|
||||
}
|
||||
return (force_forward_slash) ? output.replace(this.path.sep, '/') : output;
|
||||
}
|
||||
|
||||
makeSafeUsername(username) {
|
||||
return username.replace(/^([A-Za-z0-9\-\_]{5,16})$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Corrects any / or \ differences, if any for file paths
|
||||
* @param {string} path
|
||||
* @returns {string} corrected path
|
||||
*/
|
||||
fixPathSlashes(path) {
|
||||
// fix slashes
|
||||
if (this.path.sep == '/' && path.indexOf("\\") != -1) path = path.replace(/\\/g, this.path.sep);
|
||||
else if (this.path.sep == "\\" && path.indexOf("/") != -1) path = path.replace(/\//g, this.path.sep);
|
||||
|
||||
// remove double slashes
|
||||
while (path.indexOf(this.path.sep + this.path.sep) != -1) path = path.replace(this.path.sep + this.path.sep, this.path.sep);
|
||||
|
||||
return path;
|
||||
}
|
||||
/**
|
||||
* Makes sure an SSID is clean, and doesn't contain any exploitable characters
|
||||
* @param {string} ssid
|
||||
* @returns {string} Sanitized SSID
|
||||
*/
|
||||
makeSafeSSID(ssid = "") {
|
||||
ssid = ssid.replace(/[^a-zA-Z0-9]/g, "");
|
||||
if (ssid.length == 0) ssid = null;
|
||||
return ssid;
|
||||
}
|
||||
|
||||
|
||||
unpackCompressedB64(data) {
|
||||
var data_buf = (typeof data === 'object') ? Buffer.from(data.toString('ascii'), 'base64') : Buffer.from(data, 'base64');
|
||||
return this.zlib.inflateSync(data_buf, { finishFlush: this.zlib.Z_SYNC_FLUSH }).toString('ascii');
|
||||
}
|
||||
|
||||
packCompressedB64(data) {
|
||||
return this.zlib.deflateSync(data, { 'level': 9 }).toString('base64');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class clientShowAlert {
|
||||
message = null;
|
||||
buttonlabel1 = null;
|
||||
buttonlabel2 = null;
|
||||
buttonaction1 = null;
|
||||
buttonaction2 = null;
|
||||
noback = null;
|
||||
image = null;
|
||||
|
||||
constructor(image = null, message = null, buttonlabel1 = null, buttonaction1 = null, buttonlabel2 = null, buttonaction2 = null, noback = null) {
|
||||
this.message = message;
|
||||
this.buttonlabel1 = buttonlabel1;
|
||||
this.buttonlabel2 = buttonlabel2;
|
||||
this.buttonaction1 = buttonaction1;
|
||||
this.buttonaction2 = buttonaction2;
|
||||
this.message = message;
|
||||
this.noback = noback;
|
||||
if (typeof image === 'object') {
|
||||
this.image = null;
|
||||
Object.keys(image).forEach(function (k) {
|
||||
if (this[k] === null) this[k] = image[k];
|
||||
}, this);
|
||||
} else {
|
||||
this.image = image;
|
||||
}
|
||||
}
|
||||
|
||||
getURL() {
|
||||
var url = "client:ShowAlert?";
|
||||
if (this.message) url += "message=" + escape(this.message) + "&";
|
||||
if (this.buttonlabel1) url += "buttonlabel1=" + escape(this.buttonlabel1) + "&";
|
||||
if (this.buttonaction1) url += "buttonaction1=" + escape(this.buttonaction1) + "&";
|
||||
if (this.buttonlabel2) url += "buttonlabel2=" + escape(this.buttonlabel2) + "&";
|
||||
if (this.buttonaction2) url += "buttonaction2=" + escape(this.buttonaction2) + "&";
|
||||
if (this.image) url += "image=" + escape(this.image) + "&";
|
||||
if (this.noback) url += "noback=true&";
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.WTVShared = WTVShared;
|
||||
module.exports.clientShowAlert = clientShowAlert;
|
||||
Reference in New Issue
Block a user