more optimizations

This commit is contained in:
zefie
2025-08-06 15:21:33 -04:00
parent 931e96108b
commit f51c0b712f
4 changed files with 189 additions and 184 deletions

View File

@@ -1,13 +1,12 @@
// From https://stackoverflow.com/a/42019773 // From https://stackoverflow.com/a/42019773
'use strict'; 'use strict';
let net = require('net'); const net = require('net');
let http = require('http'); const http = require('http');
let https = require('https'); const https = require('https');
exports.createServer = (opts, handler) => { exports.createServer = (opts, handler) => {
const server = net.createServer(socket => {
let server = net.createServer(socket => {
socket.once('data', buffer => { socket.once('data', buffer => {
// Pause the socket // Pause the socket
socket.pause(); socket.pause();

View File

@@ -42,7 +42,7 @@ class WTVClientSessionData {
this.wtvmime = new WTVMime(minisrv_config); this.wtvmime = new WTVMime(minisrv_config);
this.lockdown = false; this.lockdown = false;
this.ssid = ssid; this.ssid = ssid;
this.data_store = new Array(); this.data_store = [];
this.session_store = {}; this.session_store = {};
this.lockdownWhitelist = minisrv_config.config.lockdownWhitelist; this.lockdownWhitelist = minisrv_config.config.lockdownWhitelist;
this.lockdownWhitelist.push(minisrv_config.config.unauthorized_url); this.lockdownWhitelist.push(minisrv_config.config.unauthorized_url);
@@ -90,7 +90,7 @@ class WTVClientSessionData {
clearUserSessionMemory() { clearUserSessionMemory() {
this.setUserLoggedIn(false); this.setUserLoggedIn(false);
this.data_store = new Array(); this.data_store = [];
this.session_store = {}; this.session_store = {};
this.assignFavoriteStore(); this.assignFavoriteStore();
this.assignMailStore() this.assignMailStore()
@@ -98,7 +98,7 @@ class WTVClientSessionData {
switchUserID(user_id, update_mail = true, update_ticket = true, update_favorite = true) { switchUserID(user_id, update_mail = true, update_ticket = true, update_favorite = true) {
this.user_id = parseInt(user_id); this.user_id = parseInt(user_id);
if (user_id != null) { if (user_id !== null) {
this.loadSessionData(); this.loadSessionData();
if (this.isRegistered() && update_mail) this.assignMailStore(); if (this.isRegistered() && update_mail) this.assignMailStore();
if (this.isRegistered() && update_favorite) this.assignMailStore(); if (this.isRegistered() && update_favorite) this.assignMailStore();
@@ -189,7 +189,7 @@ class WTVClientSessionData {
var self = this; var self = this;
this.fs.readdirSync(master_directory).forEach(f => { this.fs.readdirSync(master_directory).forEach(f => {
if (self.fs.lstatSync(master_directory + self.path.sep + f).isDirectory()) { if (self.fs.lstatSync(master_directory + self.path.sep + f).isDirectory()) {
if (f.substr(0, 4) == "user") { if (f.startsWith("user")) {
var user_file = this.path.resolve(master_directory + self.path.sep + f + self.path.sep + f + ".json"); var user_file = this.path.resolve(master_directory + self.path.sep + f + self.path.sep + f + ".json");
if (self.fs.existsSync(user_file)) { if (self.fs.existsSync(user_file)) {
if (f == "user0") { if (f == "user0") {
@@ -230,7 +230,7 @@ class WTVClientSessionData {
* @returns {string|boolean} Absolute path to the user's file store, or false if unregistered * @returns {string|boolean} Absolute path to the user's file store, or false if unregistered
*/ */
getUserStoreDirectory(subscriber = false, user_id = null) { getUserStoreDirectory(subscriber = false, user_id = null) {
if (user_id == null) user_id = this.user_id; if (user_id === null) user_id = this.user_id;
var userstore = this.getAccountStoreDirectory() + this.path.sep + this.ssid + this.path.sep; var userstore = this.getAccountStoreDirectory() + this.path.sep + this.ssid + this.path.sep;
if (!subscriber) userstore += "user" + user_id + this.path.sep; if (!subscriber) userstore += "user" + user_id + this.path.sep;
return this.wtvshared.getAbsolutePath(userstore) + this.path.sep; return this.wtvshared.getAbsolutePath(userstore) + this.path.sep;
@@ -340,7 +340,7 @@ class WTVClientSessionData {
} catch (e) { } catch (e) {
console.error(" # User File Store failed", e); console.error(" # User File Store failed", e);
} }
return (result === false) ? false : true; return result !== false;
} }
scrapbookExists() { scrapbookExists() {
@@ -355,7 +355,7 @@ class WTVClientSessionData {
} }
createScrapbook() { createScrapbook() {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
try { try {
if (!this.fs.existsSync(this.scrapbook_dir)) this.fs.mkdirSync(this.scrapbook_dir, { recursive: true }); if (!this.fs.existsSync(this.scrapbook_dir)) this.fs.mkdirSync(this.scrapbook_dir, { recursive: true });
return true; return true;
@@ -365,18 +365,18 @@ class WTVClientSessionData {
} }
scrapbookDir() { scrapbookDir() {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
return this.scrapbook_dir; return this.scrapbook_dir;
} }
listScrapbook() { listScrapbook() {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
const files = this.fs.readdirSync(this.scrapbook_dir); const files = this.fs.readdirSync(this.scrapbook_dir);
const filteredFiles = files.sort(function(a, b) { const filteredFiles = files.sort((a, b) => {
return a.localeCompare(b, undefined, { return a.localeCompare(b, undefined, {
numeric: true, numeric: true,
sensitivity: 'base' sensitivity: 'base'
@@ -386,15 +386,15 @@ class WTVClientSessionData {
} }
getFreeScrapbookID() { getFreeScrapbookID() {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
var id = 1; var id = 1;
var files = this.fs.readdirSync(this.scrapbook_dir); var files = this.fs.readdirSync(this.scrapbook_dir);
if (files.length == 0) { if (files.length === 0) {
return id; return id;
} }
files = files.map(file => parseInt(file.substr(0, file.indexOf('.')))); files = files.map(file => parseInt(file.slice(0, file.indexOf('.'))));
while (files.includes(id)) { while (files.includes(id)) {
id++; id++;
} }
@@ -402,7 +402,7 @@ class WTVClientSessionData {
} }
getScrapbookUsage() { getScrapbookUsage() {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
var total_size = 0; var total_size = 0;
@@ -419,7 +419,7 @@ class WTVClientSessionData {
} }
getScrapbookUsagePercent() { getScrapbookUsagePercent() {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
var total_size = this.getScrapbookUsage(); var total_size = this.getScrapbookUsage();
@@ -430,7 +430,7 @@ class WTVClientSessionData {
} }
getScrapbookImage(id) { getScrapbookImage(id) {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
var file = this.scrapbook_dir + id; var file = this.scrapbook_dir + id;
@@ -441,7 +441,7 @@ class WTVClientSessionData {
} }
getScrapbookImageType(id) { getScrapbookImageType(id) {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
var file = this.scrapbook_dir + id + ".meta"; var file = this.scrapbook_dir + id + ".meta";
@@ -459,7 +459,7 @@ class WTVClientSessionData {
addToScrapbook(filename, contentType, data) { addToScrapbook(filename, contentType, data) {
try { try {
if (this.scrapbookExists() === false) { if (!this.scrapbookExists()) {
this.createScrapbook(); this.createScrapbook();
} }
var fileout = this.scrapbook_dir + filename; var fileout = this.scrapbook_dir + filename;
@@ -546,7 +546,7 @@ class WTVClientSessionData {
else return false; else return false;
} else { } else {
if (path && expires && data) { if (path && expires && data) {
var cookie_data = new Array(); var cookie_data = {};
cookie_data['cookie'] = unescape(data); cookie_data['cookie'] = unescape(data);
cookie_data['expires'] = unescape(expires); cookie_data['expires'] = unescape(expires);
cookie_data['path'] = unescape(path); cookie_data['path'] = unescape(path);
@@ -564,7 +564,7 @@ class WTVClientSessionData {
if (domain == self.session_store.cookies[k].domain && path == self.session_store.cookies[k].path) cookie_index = k; if (domain == self.session_store.cookies[k].domain && path == self.session_store.cookies[k].path) cookie_index = k;
}); });
// otherwise add a new one // otherwise add a new one
if (cookie_index == -1) cookie_index = this.countCookies(); if (cookie_index === -1) cookie_index = this.countCookies();
this.session_store.cookies[cookie_index] = Object.assign({}, cookie_data); this.session_store.cookies[cookie_index] = Object.assign({}, cookie_data);
@@ -582,7 +582,7 @@ class WTVClientSessionData {
var self = this; var self = this;
var result = false; var result = false;
Object.keys(this.session_store['cookies']).forEach(function (k) { Object.keys(this.session_store['cookies']).forEach(function (k) {
if (result != false) return; if (result !== false) return;
if (self.session_store['cookies'][k].domain == domain && if (self.session_store['cookies'][k].domain == domain &&
self.session_store['cookies'][k].path == path) { self.session_store['cookies'][k].path == path) {
@@ -728,7 +728,7 @@ class WTVClientSessionData {
getUserPasswordEnabled() { getUserPasswordEnabled() {
if (!this.minisrv_config.config.passwords.enabled) return false; // master config override if (!this.minisrv_config.config.passwords.enabled) return false; // master config override
var enabled = this.getSessionData("subscriber_password"); var enabled = this.getSessionData("subscriber_password");
return (enabled != null && typeof enabled != undefined); // true if set, false if null/disabled return (enabled !== null && typeof enabled !== 'undefined'); // true if set, false if null/disabled
} }
validateUserPassword(passwd) { validateUserPassword(passwd) {
@@ -846,7 +846,7 @@ class WTVClientSessionData {
nick = nick.replace(/[^a-zA-Z0-9\-\_\`\^]/g, ""); nick = nick.replace(/[^a-zA-Z0-9\-\_\`\^]/g, "");
// limit nick length based on build support // limit nick length based on build support
nick = nick.substring(0, this.getMaxUsernameLength()); nick = nick.slice(0, this.getMaxUsernameLength());
// returns headers to send to client, while storing the new data in our session data. // returns headers to send to client, while storing the new data in our session data.
this.data_store['wtv-user-name'] = nick; this.data_store['wtv-user-name'] = nick;
@@ -877,7 +877,7 @@ class WTVClientSessionData {
setSessionData(key, value) { setSessionData(key, value) {
if (key === null) throw ("ClientSessionData.setSessionData(): invalid key provided"); if (key === null) throw ("ClientSessionData.setSessionData(): invalid key provided");
if (typeof (this.session_store) === 'undefined') this.session_store = new Array(); if (typeof (this.session_store) === 'undefined') this.session_store = {};
this.session_store[key] = value; this.session_store[key] = value;
this.SaveIfRegistered(); this.SaveIfRegistered();
} }
@@ -898,7 +898,7 @@ class WTVClientSessionData {
set(key, value) { set(key, value) {
if (key === null) throw ("ClientSessionData.set(): invalid key provided"); if (key === null) throw ("ClientSessionData.set(): invalid key provided");
if (typeof (this.data_store) === 'undefined') this.data_store = new Array(); if (typeof (this.data_store) === 'undefined') this.data_store = [];
this.data_store[key] = value; this.data_store[key] = value;
this.SaveIfRegistered(); this.SaveIfRegistered();
} }
@@ -957,7 +957,7 @@ class WTVClientSessionData {
}; };
var isInSubnet = function (ip, subnet) { var isInSubnet = function (ip, subnet) {
if (subnet.indexOf('/') == -1) { if (!subnet.includes('/')) {
var mask, base_ip, long_ip = this.ip2long(ip); var mask, base_ip, long_ip = this.ip2long(ip);
var mask2, base_ip2, long_ip2 = this.ip2long(ip); var mask2, base_ip2, long_ip2 = this.ip2long(ip);
return (long_ip == long_ip2); return (long_ip == long_ip2);
@@ -986,7 +986,7 @@ class WTVClientSessionData {
if (self.minisrv_config.config.ssid_ip_allow_list) { if (self.minisrv_config.config.ssid_ip_allow_list) {
if (self.minisrv_config.config.ssid_ip_allow_list[self.ssid]) { if (self.minisrv_config.config.ssid_ip_allow_list[self.ssid]) {
Object.keys(self.minisrv_config.config.ssid_ip_allow_list[self.ssid]).forEach(function (k) { Object.keys(self.minisrv_config.config.ssid_ip_allow_list[self.ssid]).forEach(function (k) {
if (self.minisrv_config.config.ssid_ip_allow_list[self.ssid][k].indexOf('/') > 0) { if (self.minisrv_config.config.ssid_ip_allow_list[self.ssid][k].includes('/')) {
if (isInSubnet(self.clientAddress, self.minisrv_config.config.ssid_ip_allow_list[self.ssid][k])) { if (isInSubnet(self.clientAddress, self.minisrv_config.config.ssid_ip_allow_list[self.ssid][k])) {
// remoteAddr is in allowed subnet // remoteAddr is in allowed subnet
ssid_access_list_ip_override = true; ssid_access_list_ip_override = true;
@@ -1011,7 +1011,7 @@ class WTVClientSessionData {
// process whitelist first // process whitelist first
if (self.ssid && self.minisrv_config.config.ssid_allow_list) { if (self.ssid && self.minisrv_config.config.ssid_allow_list) {
var ssid_is_in_whitelist = self.minisrv_config.config.ssid_allow_list.findIndex(element => element == self.ssid); var ssid_is_in_whitelist = self.minisrv_config.config.ssid_allow_list.findIndex(element => element == self.ssid);
if (ssid_is_in_whitelist == -1) { if (ssid_is_in_whitelist === -1) {
// no whitelist match, but lets see if the remoteAddress is allowed // no whitelist match, but lets see if the remoteAddress is allowed
checkSSIDIPWhitelist(self.ssid, false); checkSSIDIPWhitelist(self.ssid, false);
} }
@@ -1020,7 +1020,7 @@ class WTVClientSessionData {
// now check blacklist // now check blacklist
if (self.ssid && self.minisrv_config.config.ssid_block_list) { if (self.ssid && self.minisrv_config.config.ssid_block_list) {
var ssid_is_in_blacklist = self.minisrv_config.config.ssid_block_list.findIndex(element => element == self.ssid); var ssid_is_in_blacklist = self.minisrv_config.config.ssid_block_list.findIndex(element => element == self.ssid);
if (ssid_is_in_blacklist != -1) { if (ssid_is_in_blacklist !== -1) {
// blacklist match, but lets see if the remoteAddress is allowed // blacklist match, but lets see if the remoteAddress is allowed
checkSSIDIPWhitelist(self.ssid, true); checkSSIDIPWhitelist(self.ssid, true);
} }
@@ -1046,19 +1046,21 @@ class WTVClientSessionData {
switch (whitelist) { switch (whitelist) {
case "lockdown": case "lockdown":
Object.keys(this.lockdownWhitelist).forEach(function (k) { Object.keys(this.lockdownWhitelist).forEach(function (k) {
if (self.lockdownWhitelist[k].charAt(self.lockdownWhitelist[k].length - 1) == '*') { if (self.lockdownWhitelist[k].endsWith('*')) {
if (self.lockdownWhitelist[k].substring(0, self.lockdownWhitelist[k].length - 1) == url.substring(0, self.lockdownWhitelist[k].length - 1)) authorized = true; const prefix = self.lockdownWhitelist[k].slice(0, -1);
if (url.startsWith(prefix)) authorized = true;
} else { } else {
if (self.lockdownWhitelist[k].substring(0, url.length) == url) authorized = true; if (url.startsWith(self.lockdownWhitelist[k])) authorized = true;
} }
}); });
break; break;
case "login": case "login":
Object.keys(this.loginWhitelist).forEach(function (k) { Object.keys(this.loginWhitelist).forEach(function (k) {
if (self.loginWhitelist[k].charAt(self.loginWhitelist[k].length - 1) == '*') { if (self.loginWhitelist[k].endsWith('*')) {
if (self.loginWhitelist[k].substring(0, self.loginWhitelist[k].length - 1) == url.substring(0, self.loginWhitelist[k].length - 1)) authorized = true; const prefix = self.loginWhitelist[k].slice(0, -1);
if (url.startsWith(prefix)) authorized = true;
} else { } else {
if (self.loginWhitelist[k].substring(0, url.length) == url) authorized = true; if (url.startsWith(self.loginWhitelist[k])) authorized = true;
} }
}); });
break; break;
@@ -1099,7 +1101,7 @@ class WTVClientSessionData {
else else
return "Sony"; return "Sony";
else if (brandId == 1) else if (brandId == 1)
if (url && isPlus == true) if (url && isPlus === true)
return "Philips-Plus"; return "Philips-Plus";
else else
return "Philips"; return "Philips";

View File

@@ -1,8 +1,8 @@
const CryptoJS = require('crypto-js'); const CryptoJS = require('crypto-js');
const endianness = require('endianness'); const endianness = require('endianness');
var RC4 = require('rc4-crypto'); const RC4 = require('rc4-crypto');
var crypto = require('crypto'); const crypto = require('crypto');
var WTVShared = require("./WTVShared.js")['WTVShared']; const WTVShared = require("./WTVShared.js")['WTVShared'];
/** /**
* Javascript implementation of WTVP Security * Javascript implementation of WTVP Security
@@ -26,7 +26,7 @@ class WTVSec {
session_key2 = null; session_key2 = null;
hRC4_Key1 = null; hRC4_Key1 = null;
hRC4_Key2 = null; hRC4_Key2 = null;
RC4Session = new Array(); RC4Session = [];
minisrv_config = []; minisrv_config = [];
update_ticket = false; update_ticket = false;
wtvshared = null; wtvshared = null;
@@ -49,7 +49,7 @@ class WTVSec {
this.incarnation = wtv_incarnation; this.incarnation = wtv_incarnation;
this.current_shared_key = this.initial_shared_key; this.current_shared_key = this.initial_shared_key;
} else { } else {
throw ("Invalid initial key length"); throw new Error("Invalid initial key length");
} }
} }
@@ -58,7 +58,7 @@ class WTVSec {
* @param {Number} wtv_incarnation * @param {Number} wtv_incarnation
*/ */
set_incarnation(wtv_incarnation) { set_incarnation(wtv_incarnation) {
if (this.incarnation != wtv_incarnation) { if (this.incarnation !== wtv_incarnation) {
this.incarnation = wtv_incarnation; this.incarnation = wtv_incarnation;
this.SecureOn(); this.SecureOn();
} }
@@ -68,7 +68,7 @@ class WTVSec {
* Increments the wtv-incaration for this instance by 1 * Increments the wtv-incaration for this instance by 1
*/ */
increment_incarnation() { increment_incarnation() {
this.set_incarnation(parseInt(this.incarnation) + 1); this.set_incarnation(Number(this.incarnation) + 1);
} }
/** /**
@@ -86,21 +86,21 @@ class WTVSec {
*/ */
PrepareTicket() { PrepareTicket() {
// store last challenge response in ticket // store last challenge response in ticket
if (this.minisrv_config.config.debug_flags.debug) console.log(" * Preparing a new ticket with ticket_store:", this.ticket_store) if (this.minisrv_config.config.debug_flags.debug) console.log(" * Preparing a new ticket with ticket_store:", this.ticket_store);
var ticket_data_raw = this.challenge_raw; let ticket_data_raw = this.challenge_raw;
try { try {
var ticket_data = ticket_data_raw.toString(CryptoJS.enc.Hex) + CryptoJS.enc.Utf8.parse(JSON.stringify(this.ticket_store)).toString(CryptoJS.enc.Hex); const ticket_data = ticket_data_raw.toString(CryptoJS.enc.Hex) + CryptoJS.enc.Utf8.parse(JSON.stringify(this.ticket_store)).toString(CryptoJS.enc.Hex);
ticket_data_raw = CryptoJS.enc.Hex.parse(ticket_data); ticket_data_raw = CryptoJS.enc.Hex.parse(ticket_data);
var ticket_data_enc = CryptoJS.DES.encrypt(ticket_data_raw, this.initial_shared_key, { const ticket_data_enc = CryptoJS.DES.encrypt(ticket_data_raw, this.initial_shared_key, {
mode: CryptoJS.mode.ECB, mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7 padding: CryptoJS.pad.Pkcs7
}); });
// create a copy of WordArray since concat modifies the original // create a copy of WordArray since concat modifies the original
var challenge_signed_key = this.DuplicateWordArray(this.challenge_signed_key); const challenge_signed_key = this.DuplicateWordArray(this.challenge_signed_key);
this.ticket_b64 = challenge_signed_key.concat(ticket_data_enc.ciphertext).toString(CryptoJS.enc.Base64); this.ticket_b64 = challenge_signed_key.concat(ticket_data_enc.ciphertext).toString(CryptoJS.enc.Base64);
} catch (e) { } catch (e) {
console.log("Error encrypting ticket: " + e.toString()); console.log(`Error encrypting ticket: ${e}`);
return null; return null;
} }
return this.ticket_b64; return this.ticket_b64;
@@ -111,11 +111,11 @@ class WTVSec {
* @param {Base64} ticket_b64 * @param {Base64} ticket_b64
*/ */
DecodeTicket(ticket_b64) { DecodeTicket(ticket_b64) {
var ticket_hex = CryptoJS.enc.Base64.parse(ticket_b64).toString(CryptoJS.enc.Hex); const ticket_hex = CryptoJS.enc.Base64.parse(ticket_b64).toString(CryptoJS.enc.Hex);
var challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(0, 16)); const challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.slice(0, 16));
var challenge_enc = CryptoJS.enc.Hex.parse(ticket_hex.substring(16)); const challenge_enc = CryptoJS.enc.Hex.parse(ticket_hex.slice(16));
var ticket_dec = CryptoJS.DES.decrypt( const ticket_dec = CryptoJS.DES.decrypt(
{ {
ciphertext: challenge_enc ciphertext: challenge_enc
}, },
@@ -125,11 +125,11 @@ class WTVSec {
padding: CryptoJS.pad.Pkcs7 padding: CryptoJS.pad.Pkcs7
} }
); );
var data_offset = 216; // (108 * 2); const data_offset = 216; // (108 * 2);
var challenge_code = ticket_dec.toString().substring(0, data_offset); const challenge_code = ticket_dec.toString().slice(0, data_offset);
var challenge_code_b64 = CryptoJS.enc.Hex.parse(challenge_code).toString(CryptoJS.enc.Base64); const challenge_code_b64 = CryptoJS.enc.Hex.parse(challenge_code).toString(CryptoJS.enc.Base64);
if ((ticket_dec.sigBytes * 2) >= challenge_code.length) { if ((ticket_dec.sigBytes * 2) >= challenge_code.length) {
var ticket_data_dec = CryptoJS.enc.Hex.parse(ticket_dec.toString().substring(data_offset)).toString(CryptoJS.enc.Utf8); const ticket_data_dec = CryptoJS.enc.Hex.parse(ticket_dec.toString().slice(data_offset)).toString(CryptoJS.enc.Utf8);
this.ticket_store = this.wtvshared.tryDecodeJSON(ticket_data_dec); this.ticket_store = this.wtvshared.tryDecodeJSON(ticket_data_dec);
} else { } else {
this.ticket_store = {}; this.ticket_store = {};
@@ -145,9 +145,9 @@ class WTVSec {
* @returns {any} The ticket data for the specified key, or null if not found * @returns {any} The ticket data for the specified key, or null if not found
*/ */
getTicketData(key = null) { getTicketData(key = null) {
if (typeof (this.ticket_store) === 'session_store') return null; if (typeof this.ticket_store === 'session_store') return null;
else if (key === null) return this.ticket_store; else if (key === null) return this.ticket_store;
else if (typeof this.ticket_store[key] !== 'undefined') return this.ticket_store[key]; else if (key in this.ticket_store) return this.ticket_store[key];
else return null; else return null;
} }
@@ -157,8 +157,8 @@ class WTVSec {
* @param {any} value The value to set for the specified key * @param {any} value The value to set for the specified key
*/ */
setTicketData(key, value) { setTicketData(key, value) {
if (key === null) throw ("WTVSec.setTicketData(): invalid key provided"); if (!key) throw new Error("WTVSec.setTicketData(): invalid key provided");
if (typeof (this.ticket_store) === 'undefined') this.ticket_store = {}; if (!this.ticket_store) this.ticket_store = {};
this.ticket_store[key] = value; this.ticket_store[key] = value;
if (this.ticket_b64) this.PrepareTicket(); if (this.ticket_b64) this.PrepareTicket();
this.update_ticket = true; this.update_ticket = true;
@@ -169,8 +169,8 @@ class WTVSec {
* @param {string} key The key of the ticket data to delete * @param {string} key The key of the ticket data to delete
*/ */
deleteTicketData(key) { deleteTicketData(key) {
if (key === null) throw ("WTVSec.deleteTicketData(): invalid key provided"); if (!key) throw new Error("WTVSec.deleteTicketData(): invalid key provided");
if (typeof (this.ticket_store) === 'undefined') { if (!this.ticket_store) {
this.ticket_store = {}; this.ticket_store = {};
return; return;
} }
@@ -194,8 +194,8 @@ class WTVSec {
} }
const challenge_raw_hex = challenge_raw.toString(CryptoJS.enc.Hex); const challenge_raw_hex = challenge_raw.toString(CryptoJS.enc.Hex);
const challenge_id_hex = challenge_raw_hex.substring(0, 16); // 8 bytes * 2 const challenge_id_hex = challenge_raw_hex.slice(0, 16); // 8 bytes * 2
const challenge_enc = CryptoJS.enc.Hex.parse(challenge_raw_hex.substring(16)); const challenge_enc = CryptoJS.enc.Hex.parse(challenge_raw_hex.slice(16));
const challenge_decrypted = CryptoJS.DES.decrypt( const challenge_decrypted = CryptoJS.DES.decrypt(
{ ciphertext: challenge_enc }, { ciphertext: challenge_enc },
@@ -204,18 +204,18 @@ class WTVSec {
); );
const challenge_dec_hex = challenge_decrypted.toString(CryptoJS.enc.Hex); const challenge_dec_hex = challenge_decrypted.toString(CryptoJS.enc.Hex);
const challenge_md5_challenge = CryptoJS.MD5(CryptoJS.enc.Hex.parse(challenge_dec_hex.substring(0, 160))).toString(CryptoJS.enc.Hex); // 80 bytes * 2 const challenge_md5_challenge = CryptoJS.MD5(CryptoJS.enc.Hex.parse(challenge_dec_hex.slice(0, 160))).toString(CryptoJS.enc.Hex); // 80 bytes * 2
if (challenge_dec_hex.substring(160, 192) !== challenge_md5_challenge) { // 96 bytes * 2 if (challenge_dec_hex.slice(160, 192) !== challenge_md5_challenge) { // 96 bytes * 2
return ""; return "";
} }
this.current_shared_key = CryptoJS.enc.Hex.parse(challenge_dec_hex.substring(144, 160)); // 72 bytes * 2, 80 bytes * 2 this.current_shared_key = CryptoJS.enc.Hex.parse(challenge_dec_hex.slice(144, 160)); // 72 bytes * 2, 80 bytes * 2
const challenge_echo = CryptoJS.enc.Hex.parse(challenge_dec_hex.substr(0, 80)); // 40 bytes * 2 const challenge_echo = CryptoJS.enc.Hex.parse(challenge_dec_hex.slice(0, 80)); // 40 bytes * 2
// RC4 encryption keys. Stored in the wtv-ticket on the server side. // RC4 encryption keys. Stored in the wtv-ticket on the server side.
this.session_key1 = CryptoJS.enc.Hex.parse(challenge_dec_hex.substring(80, 112)); // 40 bytes * 2, 56 bytes * 2 this.session_key1 = CryptoJS.enc.Hex.parse(challenge_dec_hex.slice(80, 112)); // 40 bytes * 2, 56 bytes * 2
this.session_key2 = CryptoJS.enc.Hex.parse(challenge_dec_hex.substring(112, 144)); // 56 bytes * 2, 72 bytes * 2 this.session_key2 = CryptoJS.enc.Hex.parse(challenge_dec_hex.slice(112, 144)); // 56 bytes * 2, 72 bytes * 2
const echo_encrypted = CryptoJS.DES.encrypt( const echo_encrypted = CryptoJS.DES.encrypt(
CryptoJS.MD5(challenge_echo).concat(challenge_echo).concat(CryptoJS.enc.Utf8.parse("\x08".repeat(8))), CryptoJS.MD5(challenge_echo).concat(challenge_echo).concat(CryptoJS.enc.Utf8.parse("\x08".repeat(8))),
@@ -279,14 +279,14 @@ class WTVSec {
* @param {Number} rc4session Session Type (0 = enc k1, 1 = dec k1, 2 = enc k2, 3 = dec k2, default: all) * @param {Number} rc4session Session Type (0 = enc k1, 1 = dec k1, 2 = enc k2, 3 = dec k2, default: all)
*/ */
SecureOn(rc4session = null) { SecureOn(rc4session = null) {
if (this.minisrv_config.config.debug_flags.debug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation); if (this.minisrv_config.config.debug_flags.debug) console.log(` # Generating RC4 sessions with wtv-incarnation: ${this.incarnation}`);
var buf = new Uint8Array([0xff & this.incarnation, 0xff & (this.incarnation >> 8), 0xff & (this.incarnation >> 16), 0xff & (this.incarnation >> 24)]); const buf = new Uint8Array([0xff & this.incarnation, 0xff & (this.incarnation >> 8), 0xff & (this.incarnation >> 16), 0xff & (this.incarnation >> 24)]);
endianness(buf, 4); endianness(buf, 4);
this.hRC4_Key1 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key1).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key1)))); this.hRC4_Key1 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key1).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key1))));
this.hRC4_Key2 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key2).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key2)))); this.hRC4_Key2 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key2).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key2))));
var key1 = this.wtvshared.wordArrayToBuffer(this.hRC4_Key1); const key1 = this.wtvshared.wordArrayToBuffer(this.hRC4_Key1);
var key2 = this.wtvshared.wordArrayToBuffer(this.hRC4_Key2); const key2 = this.wtvshared.wordArrayToBuffer(this.hRC4_Key2);
const setRC4Session = (sessionIndex, key) => { const setRC4Session = (sessionIndex, key) => {
this.RC4Session[sessionIndex] = new RC4.RC4(key); this.RC4Session[sessionIndex] = new RC4.RC4(key);
}; };

View File

@@ -30,22 +30,22 @@ class WTVShared {
* @notice If minisrv_config is null, it will attempt to read the configuration from the minisrv_config.json file * @notice If minisrv_config is null, it will attempt to read the configuration from the minisrv_config.json file
* */ * */
constructor(minisrv_config, quiet = false) { constructor(minisrv_config, quiet = false) {
if (minisrv_config == null) this.minisrv_config = this.readMiniSrvConfig(true, !quiet); if (minisrv_config === null || typeof minisrv_config === 'undefined') this.minisrv_config = this.readMiniSrvConfig(true, !quiet);
else this.minisrv_config = minisrv_config; else this.minisrv_config = minisrv_config;
this.shenanigans = new WTVShenanigans(this.minisrv_config); this.shenanigans = new WTVShenanigans(this.minisrv_config);
if (!String.prototype.reverse) { if (!String.prototype.reverse) {
String.prototype.reverse = function () { String.prototype.reverse = function () {
var splitString = this.split(""); const splitString = this.split("");
var reverseArray = splitString.reverse(); const reverseArray = splitString.reverse();
return reverseArray.join(""); return reverseArray.join("");
} }
} }
if (!String.prototype.hexEncode) { if (!String.prototype.hexEncode) {
String.prototype.hexEncode = function () { String.prototype.hexEncode = function () {
var result = ''; let result = '';
for (var i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
result += this.charCodeAt(i).toString(16); result += this.charCodeAt(i).toString(16);
} }
return result; return result;
@@ -59,7 +59,7 @@ class WTVShared {
* @returns {Buffer} JS Buffer object * @returns {Buffer} JS Buffer object
*/ */
wordArrayToBuffer(wordArray) { wordArrayToBuffer(wordArray) {
if (wordArray) return new Buffer.from(wordArray.toString(CryptoJS.enc.Hex), 'hex'); if (wordArray) return Buffer.from(wordArray.toString(CryptoJS.enc.Hex), 'hex');
else return null; else return null;
} }
@@ -92,12 +92,12 @@ class WTVShared {
* @returns {number} The long integer representation of the IP address, or -1 if the IP address is invalid * @returns {number} The long integer representation of the IP address, or -1 if the IP address is invalid
*/ */
ip2long(ip) { ip2long(ip) {
var components; let components;
if (components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)) { if (components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)) {
var iplong = 0; let iplong = 0;
var power = 1; let power = 1;
for (var i = 4; i >= 1; i -= 1) { for (let i = 4; i >= 1; i -= 1) {
iplong += power * parseInt(components[i]); iplong += power * parseInt(components[i], 10);
power *= 256; power *= 256;
} }
return iplong; return iplong;
@@ -112,15 +112,19 @@ class WTVShared {
* @returns {boolean} True if the IP address is in the subnet, false otherwise * @returns {boolean} True if the IP address is in the subnet, false otherwise
*/ */
isInSubnet(ip, subnet) { isInSubnet(ip, subnet) {
if (subnet.indexOf('/') == -1) { if (!subnet.includes('/')) {
var mask, base_ip, long_ip = this.ip2long(ip); const long_ip = this.ip2long(ip);
var mask2, base_ip2, long_ip2 = this.ip2long(ip); const long_subnet = this.ip2long(subnet);
return (long_ip == long_ip2); return long_ip === long_subnet;
} else { } else {
var mask, base_ip, long_ip = this.ip2long(ip); const long_ip = this.ip2long(ip);
if ((mask = subnet.match(/^(.*?)\/(\d{1,2})$/)) && ((base_ip = this.ip2long(mask[1])) >= 0)) { const mask = subnet.match(/^(.*?)\/(\d{1,2})$/);
var freedom = Math.pow(2, 32 - parseInt(mask[2])); if (mask && mask[1]) {
return (long_ip > base_ip) && (long_ip < base_ip + freedom - 1); const base_ip = this.ip2long(mask[1]);
if (base_ip >= 0) {
const freedom = 2 ** (32 - parseInt(mask[2], 10));
return (long_ip >= base_ip) && (long_ip <= base_ip + freedom - 1);
}
} }
} }
return false; return false;
@@ -162,7 +166,7 @@ class WTVShared {
* @returns {Object} The decoded JSON object, or an empty object if decoding fails. * @returns {Object} The decoded JSON object, or an empty object if decoding fails.
*/ */
tryDecodeJSON(json_string) { tryDecodeJSON(json_string) {
var out; let out;
try { try {
out = JSON.parse(json_string); out = JSON.parse(json_string);
} catch (e) { } catch (e) {
@@ -209,7 +213,7 @@ class WTVShared {
let crc = 0; let crc = 0;
for (let i = 0; i < 14; i += 2) { for (let i = 0; i < 14; i += 2) {
let inbyte = parseInt(ssid.substring(i, i + 2), 16); let inbyte = parseInt(ssid.slice(i, i + 2), 16);
if (isNaN(inbyte)) return '00'; if (isNaN(inbyte)) return '00';
for (let ii = 0; ii < 8; ii++) { for (let ii = 0; ii < 8; ii++) {
@@ -240,7 +244,7 @@ class WTVShared {
* @returns {string} The parsed string * @returns {string} The parsed string
*/ */
parseConfigVars(s) { parseConfigVars(s) {
if (s.indexOf("%ServiceDeps%") >= 0) if (s.includes("%ServiceDeps%"))
return this.getServiceDep(s.replace("%ServiceDeps%", ""), true); return this.getServiceDep(s.replace("%ServiceDeps%", ""), true);
else else
return s; return s;
@@ -279,11 +283,11 @@ class WTVShared {
} else if (src instanceof Date) { } else if (src instanceof Date) {
return new Date(src.getTime()); return new Date(src.getTime());
} else if (typeof src === 'object' && src !== null) { } else if (typeof src === 'object' && src !== null) {
var clone = null; let clone = null;
if (Array.isArray(src)) clone = []; if (Array.isArray(src)) clone = [];
else clone = {}; else clone = {};
var self = this; const self = this;
Object.keys(src).forEach((k) => { Object.keys(src).forEach((k) => {
clone[k] = self.cloneObj(src[k]); clone[k] = self.cloneObj(src[k]);
}); });
@@ -298,10 +302,9 @@ class WTVShared {
* @param {string} service_name (optional) Service to check * @param {string} service_name (optional) Service to check
*/ */
isAdmin(wtvclient, service_name = "wtv-admin") { isAdmin(wtvclient, service_name = "wtv-admin") {
var WTVAdmin = require("./WTVAdmin.js"); const WTVAdmin = require("./WTVAdmin.js");
var wtva = new WTVAdmin(this.minisrv_config, wtvclient, service_name); const wtva = new WTVAdmin(this.minisrv_config, wtvclient, service_name);
var result = wtva.isAuthorized(true); const result = wtva.isAuthorized(true);
wtva, WTVAdmin = null;
return result; return result;
} }
@@ -360,7 +363,7 @@ class WTVShared {
// for easy retrofitting old code to work with the webtvism of allowing multiple of the same query name // 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 // 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 its an array we just pull the last object
if (typeof (query) === "object") if (typeof query === "object")
return query[(Object.keys(query).length - 1)]; return query[(Object.keys(query).length - 1)];
else else
return query return query
@@ -379,7 +382,7 @@ class WTVShared {
} }
// Directly replace &, <, >, ", and ' characters // Directly replace &, <, >, ", and ' characters
let entitized = string.replace(/[&<>"]/g, function (match) { let entitized = string.replace(/[&<>"]/g, (match) => {
switch (match) { switch (match) {
case '&': return '&amp;'; case '&': return '&amp;';
case '<': return '&lt;'; case '<': return '&lt;';
@@ -404,11 +407,11 @@ class WTVShared {
* @returns {string} Sanitized string * @returns {string} Sanitized string
*/ */
sanitizeSignature(string) { sanitizeSignature(string) {
var allowedSchemes = ['http', 'https', 'ftp', 'mailto']; const allowedSchemes = ['http', 'https', 'ftp', 'mailto'];
var self = this; const self = this;
// allow links to services flagged as "wideopen" // allow links to services flagged as "wideopen"
Object.keys(this.minisrv_config.services).forEach((k) => { Object.keys(this.minisrv_config.services).forEach((k) => {
var flag = parseInt(this.minisrv_config.services[k].flags, 16); const flag = parseInt(this.minisrv_config.services[k].flags, 16);
if (flag === 4 || flag === 7) { if (flag === 4 || flag === 7) {
if (!allowedSchemes.includes(k)) allowedSchemes.push(k); if (!allowedSchemes.includes(k)) allowedSchemes.push(k);
} }
@@ -434,12 +437,12 @@ class WTVShared {
allowedSchemes: allowedSchemes, allowedSchemes: allowedSchemes,
allowedSchemesByTag: {}, allowedSchemesByTag: {},
allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'], allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],
exclusiveFilter: function (frame) { exclusiveFilter: (frame) => {
var allowed = true; let allowed = true;
Object.keys(frame.attribs).forEach((k) => { Object.keys(frame.attribs).forEach((k) => {
if (k == "href" || k == "background" || k == "src") { if (k == "href" || k == "background" || k == "src") {
allowed = false; allowed = false;
var value = frame.attribs[k]; const value = frame.attribs[k];
if (frame.tag !== "a") { if (frame.tag !== "a") {
// check everything except normal links // check everything except normal links
@@ -473,33 +476,33 @@ class WTVShared {
* @return {object} Headers object * @return {object} Headers object
*/ */
headerStringToObj(headers, response = false) { headerStringToObj(headers, response = false) {
var inc_headers = 0; let inc_headers = 0;
var headers_obj = {}; const headers_obj = {};
headers_obj.raw_headers = headers; headers_obj.raw_headers = headers;
var headers_obj_pre = headers.split("\n"); const headers_obj_pre = headers.split("\n");
headers_obj_pre.forEach(function (d) { headers_obj_pre.forEach((d) => {
if (/^SECURE ON/.test(d) && !response) { if (/^SECURE ON/.test(d) && !response) {
headers_obj.secure = true; headers_obj.secure = true;
} else if (/^([0-9]{3}) $/.test(d.substring(0, 4)) && response && !headers_obj.Status) { } else if (/^([0-9]{3}) $/.test(d.slice(0, 4)) && response && !headers_obj.Status) {
d.s d.s
headers_obj.Status = d.trim("\r"); headers_obj.Status = d.trim("\r");
} else if (/^(GET |PUT |POST)$/.test(d.substring(0, 4)) && !response) { } else if (/^(GET |PUT |POST)$/.test(d.slice(0, 4)) && !response) {
headers_obj.request = d.trim("\r"); headers_obj.request = d.trim("\r");
var request_url = d.split(' '); let request_url = d.split(' ');
if (request_url.length > 2) { if (request_url.length > 2) {
request_url.shift(); request_url.shift();
request_url = request_url.join(" "); request_url = request_url.join(" ");
if (request_url.indexOf("HTTP/") > 0) { if (request_url.indexOf("HTTP/") > 0) {
var index = request_url.indexOf(" HTTP/"); const index = request_url.indexOf(" HTTP/");
request_url = request_url.substring(0, index); request_url = request_url.slice(0, index);
} }
} else { } else {
request_url = request_url[1]; request_url = request_url[1];
} }
headers_obj.request_url = decodeURI(request_url).trim("\r"); headers_obj.request_url = decodeURI(request_url).trim("\r");
} else if (d.indexOf(":") > 0) { } else if (d.indexOf(":") > 0) {
var d_split = d.split(':'); const d_split = d.split(':');
var header_name = d_split[0]; let header_name = d_split[0];
if (headers_obj[header_name] != null) { if (headers_obj[header_name] != null) {
header_name = header_name + "_" + inc_headers; header_name = header_name + "_" + inc_headers;
inc_headers++; inc_headers++;
@@ -507,8 +510,8 @@ class WTVShared {
d_split.shift(); d_split.shift();
d = d_split.join(':'); d = d_split.join(':');
headers_obj[header_name] = (d).trim("\r"); headers_obj[header_name] = (d).trim("\r");
if (headers_obj[header_name].substring(0, 1) == " ") { if (headers_obj[header_name].startsWith(" ")) {
headers_obj[header_name] = headers_obj[header_name].substring(1); headers_obj[header_name] = headers_obj[header_name].slice(1);
} }
} }
}); });
@@ -522,17 +525,17 @@ class WTVShared {
* @returns {object} // Headers object with only whitelisted headers * @returns {object} // Headers object with only whitelisted headers
*/ */
stripHeaders(headers_obj, whitelist) { stripHeaders(headers_obj, whitelist) {
var whitelisted_headers = new Array(); const whitelisted_headers = [];
var out_headers = new Array(); const out_headers = [];
out_headers.Status = headers_obj.Status; out_headers.Status = headers_obj.Status;
if (headers_obj['wtv-connection-close']) out_headers['wtv-connection-close'] = headers_obj['wtv-connection-close']; if (headers_obj['wtv-connection-close']) out_headers['wtv-connection-close'] = headers_obj['wtv-connection-close'];
// compare regardless of case // compare regardless of case
Object.keys(whitelist).forEach(function (k) { Object.keys(whitelist).forEach((k) => {
Object.keys(headers_obj).forEach(function (j) { Object.keys(headers_obj).forEach((j) => {
if (whitelist[k].toLowerCase() == j.toLowerCase()) { if (whitelist[k].toLowerCase() === j.toLowerCase()) {
// if header = connection, strip 'upgrade' // if header = connection, strip 'upgrade'
if (j.toLowerCase() == "connection") { if (j.toLowerCase() === "connection") {
headers_obj[j] = headers_obj[j].replace("Upgrade", "").replace(",", "").trim(); headers_obj[j] = headers_obj[j].replace("Upgrade", "").replace(",", "").trim();
} }
whitelisted_headers[j.toLowerCase()] = [whitelist[k], j, headers_obj[j]]; whitelisted_headers[j.toLowerCase()] = [whitelist[k], j, headers_obj[j]];
@@ -541,9 +544,9 @@ class WTVShared {
}); });
// restore original header order // restore original header order
Object.keys(headers_obj).forEach(function (k) { Object.keys(headers_obj).forEach((k) => {
if (whitelisted_headers[k.toLowerCase()]) { if (whitelisted_headers[k.toLowerCase()]) {
if (whitelisted_headers[k.toLowerCase()][1] == k) out_headers[whitelisted_headers[k.toLowerCase()][0]] = whitelisted_headers[k.toLowerCase()][2]; if (whitelisted_headers[k.toLowerCase()][1] === k) out_headers[whitelisted_headers[k.toLowerCase()][0]] = whitelisted_headers[k.toLowerCase()][2];
} }
}); });
@@ -712,15 +715,15 @@ class WTVShared {
}; };
const ssid_obj = { const ssid_obj = {
boxType: boxTypeMapping[ssid.substring(0, 2)], boxType: boxTypeMapping[ssid.slice(0, 2)],
unique_id: ssid.substring(2, 8), unique_id: ssid.slice(2, 8),
region: regionMapping[ssid.substring(10, 14).toUpperCase()], region: regionMapping[ssid.slice(10, 14).toUpperCase()],
manufacturer: manufacturerMapping[ssid.substring(8, 10).toUpperCase()], manufacturer: manufacturerMapping[ssid.slice(8, 10).toUpperCase()],
crc: ssid.substring(14) crc: ssid.slice(14)
}; };
// Special case for manufacturer based on region // Special case for manufacturer based on region
if (ssid_obj.region === "Japan" && ssid.substring(8, 10).toUpperCase() === "00") { if (ssid_obj.region === "Japan" && ssid.slice(8, 10).toUpperCase() === "00") {
ssid_obj.manufacturer = "Panasonic"; ssid_obj.manufacturer = "Panasonic";
} }
@@ -734,7 +737,7 @@ class WTVShared {
* @return {string} * @return {string}
*/ */
getManufacturer(ssid, bit = false) { getManufacturer(ssid, bit = false) {
if (bit) return ssid.substring(8, 10).toUpperCase(); if (bit) return ssid.slice(8, 10).toUpperCase();
else return this.parseSSID(ssid).manufacturer || null; else return this.parseSSID(ssid).manufacturer || null;
} }
@@ -930,7 +933,7 @@ class WTVShared {
* @param {string} file Path to a file * @param {string} file Path to a file
*/ */
getFileLastModified(file) { getFileLastModified(file) {
var stats = this.fs.lstatSync(file); const stats = this.fs.lstatSync(file);
if (stats) return new Date(stats.mtimeMs); if (stats) return new Date(stats.mtimeMs);
return false; return false;
} }
@@ -1026,7 +1029,7 @@ class WTVShared {
for (let i = 0; i < encoded.length; i++) { for (let i = 0; i < encoded.length; i++) {
if (encoded[i] === '%') { if (encoded[i] === '%') {
decoded[bufferIndex++] = parseInt(encoded.substr(i + 1, 2), 16); decoded[bufferIndex++] = parseInt(encoded.slice(i + 1, i + 3), 16);
i += 2; // Skip the next two characters i += 2; // Skip the next two characters
} else { } else {
decoded[bufferIndex++] = encoded.charCodeAt(i); decoded[bufferIndex++] = encoded.charCodeAt(i);
@@ -1059,7 +1062,7 @@ class WTVShared {
* @param {string|Array} obj SSID String or Headers Object * @param {string|Array} obj SSID String or Headers Object
*/ */
filterSSID(obj) { filterSSID(obj) {
var new_obj = false; let new_obj = false;
if (this.minisrv_config.config.hide_ssid_in_logs) { if (this.minisrv_config.config.hide_ssid_in_logs) {
if (typeof obj === "string") { if (typeof obj === "string") {
return this.censorSSID(obj); return this.censorSSID(obj);
@@ -1081,7 +1084,7 @@ class WTVShared {
* */ * */
filterRequestLog(obj) { filterRequestLog(obj) {
const passwordRegex = /(^pass$|passw(or)?d)/i; const passwordRegex = /(^pass$|passw(or)?d)/i;
var newobj = this.cloneObj(obj); // Clone the object once at the beginning const newobj = this.cloneObj(obj); // Clone the object once at the beginning
if (newobj.query) { if (newobj.query) {
Object.keys(newobj.query).forEach((k) => { Object.keys(newobj.query).forEach((k) => {
@@ -1151,7 +1154,7 @@ class WTVShared {
* @param {string} directory Root directory * @param {string} directory Root directory
*/ */
getAbsolutePath(path = '', directory = '.') { getAbsolutePath(path = '', directory = '.') {
if (directory[0] == "/" || directory.substr(1, 2) == ":" + this.path.sep) { if (directory[0] == "/" || directory.slice(1, 3) == ":" + this.path.sep) {
return this.path.resolve(directory + this.path.sep + path); return this.path.resolve(directory + this.path.sep + path);
} }
try { try {
@@ -1196,7 +1199,7 @@ class WTVShared {
* @return {string} path without gz, or unmodified path if it isnt a gz * @return {string} path without gz, or unmodified path if it isnt a gz
*/ */
getFilePath(path) { getFilePath(path) {
var path_split = path.split(this.path.sep); const path_split = path.split(this.path.sep);
path_split.pop(); path_split.pop();
return path_split.join(this.path.sep); return path_split.join(this.path.sep);
} }
@@ -1266,14 +1269,14 @@ class WTVShared {
getServiceString(service, overrides = {}) { getServiceString(service, overrides = {}) {
// used externally by service scripts // used externally by service scripts
if (service === "all") { if (service === "all") {
var self = this; const self = this;
var out = ""; let out = "";
Object.keys(this.minisrv_config.services).sort().forEach(function (k) { Object.keys(this.minisrv_config.services).sort().forEach((k) => {
if (!self.isConfiguredService(k)) return true; if (!self.isConfiguredService(k)) return true;
if (self.minisrv_config.services[k].pc_services) return true; if (self.minisrv_config.services[k].pc_services) return true;
if (overrides.exceptions) { if (overrides.exceptions) {
Object.keys(overrides.exceptions).forEach(function (j) { Object.keys(overrides.exceptions).forEach((j) => {
if (k != overrides.exceptions[j]) out += self.minisrv_config.services[k].toString(overrides) + "\n"; if (k != overrides.exceptions[j]) out += self.minisrv_config.services[k].toString(overrides) + "\n";
}); });
} else { } else {
@@ -1283,7 +1286,7 @@ class WTVShared {
return out; return out;
} else { } else {
if (!this.minisrv_config.services[service]) { if (!this.minisrv_config.services[service]) {
throw ("SERVICE ERROR: Attempted to provision unconfigured service: " + service) throw new Error("SERVICE ERROR: Attempted to provision unconfigured service: " + service);
} else { } else {
return this.minisrv_config.services[service].toString(overrides); return this.minisrv_config.services[service].toString(overrides);
} }
@@ -1296,12 +1299,12 @@ class WTVShared {
* @returns [headers, data] * @returns [headers, data]
*/ */
doRedirect(url) { doRedirect(url) {
var headers = [] const headers = [];
headers['Status'] = "302 Moved"; headers['Status'] = "302 Moved";
headers["Location"] = url; headers["Location"] = url;
headers["wtv-visit"] = url; headers["wtv-visit"] = url;
var data = '' const data = '';
return [headers, data] return [headers, data];
} }
/** /**
@@ -1318,7 +1321,7 @@ class WTVShared {
const message = data || errorMessage.replace(/\$\{(\w+)\}/g, (match, p1) => minisrv_config.config[p1] || ''); const message = data || errorMessage.replace(/\$\{(\w+)\}/g, (match, p1) => minisrv_config.config[p1] || '');
if (details && [400, 500].includes(code)) { if (details && [400, 500].includes(code)) {
data += "<br>Details:<br>" + details; data += `<br>Details:<br>${details}`;
} }
let headers = `Status: ${(pc_mode) ? 'HTTP/1.1' : ''} ${code} ${message}\n`; let headers = `Status: ${(pc_mode) ? 'HTTP/1.1' : ''} ${code} ${message}\n`;
@@ -1342,14 +1345,15 @@ class WTVShared {
* @param {string} target Sub path * @param {string} target Sub path
*/ */
makeSafePath(base, target = null, force_forward_slash = false) { makeSafePath(base, target = null, force_forward_slash = false) {
let output = null;
if (target) { if (target) {
target.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, ""); target.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, "");
var targetPath = this.path.posix.normalize(target) const targetPath = this.path.posix.normalize(target)
var output = this.fixPathSlashes(base + this.path.sep + targetPath); output = this.fixPathSlashes(base + this.path.sep + targetPath);
} else { } else {
base.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, ""); base.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, "");
var targetPath = this.path.posix.normalize(base) const targetPath = this.path.posix.normalize(base)
var output = this.fixPathSlashes(targetPath); output = this.fixPathSlashes(targetPath);
} }
return (force_forward_slash) ? output.replace(this.path.sep, '/') : output; return (force_forward_slash) ? output.replace(this.path.sep, '/') : output;
} }
@@ -1384,7 +1388,7 @@ class WTVShared {
*/ */
makeSafeSSID(ssid = "") { makeSafeSSID(ssid = "") {
ssid = ssid.replace(/[^a-zA-Z0-9]/g, ""); ssid = ssid.replace(/[^a-zA-Z0-9]/g, "");
if (ssid.length == 0) ssid = null; if (ssid.length === 0) ssid = null;
return ssid; return ssid;
} }
@@ -1395,7 +1399,7 @@ class WTVShared {
* */ * */
makeSafeStringPath(path = "") { makeSafeStringPath(path = "") {
path = path.replace(/[^\w]/g, "").replace(/\.\./g, ""); path = path.replace(/[^\w]/g, "").replace(/\.\./g, "");
if (path.length == 0) path = null; if (path.length === 0) path = null;
return path; return path;
} }
@@ -1405,7 +1409,7 @@ class WTVShared {
* @returns {string} Uncompressed string * @returns {string} Uncompressed string
* */ * */
unpackCompressedB64(data) { unpackCompressedB64(data) {
var data_buf = (typeof data === 'object') ? Buffer.from(data.toString('ascii'), 'base64') : Buffer.from(data, 'base64'); const 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'); return this.zlib.inflateSync(data_buf, { finishFlush: this.zlib.Z_SYNC_FLUSH }).toString('ascii');
} }
@@ -1425,14 +1429,14 @@ class WTVShared {
* @param {boolean} template If true, looks under templates subdir. * @param {boolean} template If true, looks under templates subdir.
*/ */
getServiceDep(file, path_only = false, template = false) { getServiceDep(file, path_only = false, template = false) {
var self = this; const self = this;
var outdata = null; let outdata = null;
var found = false let found = false
this.minisrv_config.config.ServiceDeps.forEach(function (dep_vault_dir) { this.minisrv_config.config.ServiceDeps.forEach((dep_vault_dir) => {
if (found) return; if (found) return;
if (template) dep_vault_dir += self.path.sep + "templates"; if (template) dep_vault_dir += self.path.sep + "templates";
var search = self.getAbsolutePath(file, dep_vault_dir); const search = self.getAbsolutePath(file, dep_vault_dir);
if (self.fs.existsSync(search)) { if (self.fs.existsSync(search)) {
if (path_only) outdata = search; if (path_only) outdata = search;
else outdata = self.fs.readFileSync(search); else outdata = self.fs.readFileSync(search);
@@ -1482,10 +1486,10 @@ class WTVShared {
let keys = Object.keys(obj); let keys = Object.keys(obj);
let values = Object.values(obj); let values = Object.values(obj);
const currentIndex = typeof currentKey === 'string' ? this.findObjectKeyIndex(currentKey, obj, case_insensitive) : parseInt(currentKey); const currentIndex = typeof currentKey === 'string' ? this.findObjectKeyIndex(currentKey, obj, case_insensitive) : +currentKey;
if (currentIndex === -1) return obj; if (currentIndex === -1) return obj;
var destIndex = typeof destKey === 'string' ? this.findObjectKeyIndex(destKey, obj, case_insensitive) : parseInt(destKey); let destIndex = typeof destKey === 'string' ? this.findObjectKeyIndex(destKey, obj, case_insensitive) : +destKey;
// Bump by one if the destKey is a string (put after the key) // Bump by one if the destKey is a string (put after the key)
if (typeof destKey === 'string' && destIndex !== -1) destIndex++; if (typeof destKey === 'string' && destIndex !== -1) destIndex++;
@@ -1555,9 +1559,9 @@ class clientShowAlert {
if (typeof image === 'object') { if (typeof image === 'object') {
this.image = null; this.image = null;
Object.keys(image).forEach(function (k) { Object.keys(image).forEach((k) => {
if (this[k] === null) this[k] = image[k]; if (this[k] === null) this[k] = image[k];
}, this); });
} else { } else {
this.image = image; this.image = image;
} }
@@ -1568,16 +1572,16 @@ class clientShowAlert {
* @returns {string} client:showalert URL * @returns {string} client:showalert URL
*/ */
getURL() { getURL() {
var url = "client:ShowAlert?"; let url = "client:ShowAlert?";
if (this.message) url += "message=" + escape(this.message) + "&"; if (this.message) url += `message=${escape(this.message)}&`;
if (this.buttonlabel1) url += "buttonlabel1=" + escape(this.buttonlabel1) + "&"; if (this.buttonlabel1) url += `buttonlabel1=${escape(this.buttonlabel1)}&`;
if (this.buttonaction1) url += "buttonaction1=" + escape(this.buttonaction1) + "&"; if (this.buttonaction1) url += `buttonaction1=${escape(this.buttonaction1)}&`;
if (this.buttonlabel2) url += "buttonlabel2=" + escape(this.buttonlabel2) + "&"; if (this.buttonlabel2) url += `buttonlabel2=${escape(this.buttonlabel2)}&`;
if (this.buttonaction2) url += "buttonaction2=" + escape(this.buttonaction2) + "&"; if (this.buttonaction2) url += `buttonaction2=${escape(this.buttonaction2)}&`;
if (this.image) url += "image=" + escape(this.image) + "&"; if (this.image) url += `image=${escape(this.image)}&`;
if (this.sound) url += "sound=" + escape(this.sound) + "&"; if (this.sound) url += `sound=${escape(this.sound)}&`;
if (this.noback) url += "noback=true&"; if (this.noback) url += "noback=true&";
return url.substring(0, url.length - 1); return url.slice(0, -1);
} }
} }