v0.9.59
- code cleanup and refactoring - implement max post size on pc services - add box account transfer system - create directory indexer for pc services - fix catchall for pc services - pc services not respects `disabled: true` if sharing the same port - new wtv-tricks:/info from WebTV Redialed - Added missing Pagebuilder themes - Fixed various PageBuilder bugs, pages should work correctly (republish your page if needed) - various security and bug fixes
This commit is contained in:
@@ -274,7 +274,7 @@ ${thisblock.title}
|
||||
block += "<td>"
|
||||
|
||||
block += `<CENTER>
|
||||
<IMG SRC="wtv-author:/${btoa(thisblock.photo)}">
|
||||
<IMG SRC="wtv-author:/${atob(thisblock.photo)}">
|
||||
</CENTER>
|
||||
</TD>
|
||||
</TR>
|
||||
@@ -470,8 +470,8 @@ this.fs.writeFile(destDir + this.wtvclient.session_store.subscriber_username + '
|
||||
break;
|
||||
|
||||
case "clipart":
|
||||
this.fs.mkdirSync(destDir + this.wtvclient.session_store.subscriber_username + '/' + pagedata.publishname + "/" + btoa(thisblock.photo).substr(0, btoa(thisblock.photo).lastIndexOf("/")), { recursive: true })
|
||||
this.fs.copyFile('includes/ServiceVault/wtv-author/' + btoa(thisblock.photo), destDir + this.wtvclient.session_store.subscriber_username + '/' + pagedata.publishname + "/" + btoa(thisblock.photo), (err) => {
|
||||
this.fs.mkdirSync(destDir + this.wtvclient.session_store.subscriber_username + '/' + pagedata.publishname + "/" + atob(thisblock.photo).substr(0, atob(thisblock.photo).lastIndexOf("/")), { recursive: true })
|
||||
this.fs.copyFile('includes/ServiceVault/wtv-author/' + atob(thisblock.photo), destDir + this.wtvclient.session_store.subscriber_username + '/' + pagedata.publishname + "/" + atob(thisblock.photo), (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
block = `<p><TABLE nocolor width=100%>`
|
||||
@@ -486,7 +486,7 @@ ${thisblock.title}
|
||||
</TD>
|
||||
</TR>`
|
||||
block += `<TR><td><CENTER>
|
||||
<IMG SRC="${btoa(thisblock.photo)}">
|
||||
<IMG SRC="${atob(thisblock.photo)}">
|
||||
</CENTER>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
@@ -52,6 +52,7 @@ class WTVClientSessionData {
|
||||
this.loginWhitelist = Object.assign([], this.lockdownWhitelist); // clone lockdown whitelist into login whitelist
|
||||
this.loginWhitelist.push("wtv-head-waiter:/choose-user");
|
||||
this.loginWhitelist.push("wtv-head-waiter:/password");
|
||||
this.loginWhitelist.push("wtv-head-waiter:/confirm-transfer");
|
||||
}
|
||||
|
||||
assignMailStore() {
|
||||
@@ -170,8 +171,7 @@ class WTVClientSessionData {
|
||||
|
||||
var master_directory = this.getUserStoreDirectory(true);
|
||||
var account_data = [];
|
||||
var self = this;
|
||||
this.debug(this.ssid)
|
||||
var self = this;
|
||||
this.fs.readdirSync(master_directory).forEach(f => {
|
||||
if (self.fs.lstatSync(master_directory + self.path.sep + f).isDirectory()) {
|
||||
if (f.substr(0, 4) == "user") {
|
||||
@@ -234,6 +234,67 @@ class WTVClientSessionData {
|
||||
return false;
|
||||
}
|
||||
|
||||
setPendingTransfer(ssid) {
|
||||
var pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||
var ssidobj = { "ssid": ssid, "type": "source" };
|
||||
this.fs.writeFileSync(pending_file, JSON.stringify(ssidobj));
|
||||
var new_userstore = this.getAccountStoreDirectory() + this.path.sep + ssidobj.ssid;
|
||||
if (!this.fs.existsSync(new_userstore)) this.fs.mkdirSync(new_userstore);
|
||||
var dest_pending_file = new_userstore + this.path.sep + "pending_transfer.json";
|
||||
var ssidobj = { "ssid": this.ssid, "type": "target" };
|
||||
this.fs.writeFileSync(dest_pending_file, JSON.stringify(ssidobj));
|
||||
}
|
||||
|
||||
cancelPendingTransfer() {
|
||||
var pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||
if (this.fs.existsSync(pending_file)) {
|
||||
var file = this.fs.readFileSync(pending_file)
|
||||
var ssidobj = JSON.parse(file);
|
||||
var new_userstore = this.getAccountStoreDirectory() + this.path.sep + ssidobj.ssid;
|
||||
var dest_pending_file = new_userstore + this.path.sep + "pending_transfer.json";
|
||||
if (this.fs.existsSync(dest_pending_file)) this.fs.unlinkSync(dest_pending_file);
|
||||
this.fs.unlinkSync(pending_file);
|
||||
if (this.fs.existsSync(new_userstore)) this.fs.rmdirSync(new_userstore);
|
||||
return ssidobj.ssid
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
finalizePendingTransfer() {
|
||||
var pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||
var file = this.fs.readFileSync(pending_file)
|
||||
var ssidobj = JSON.parse(file);
|
||||
if (ssidobj.type != "target") return false; // Only allow completion from target
|
||||
var source_ssid = ssidobj.ssid
|
||||
var old_account = this.getAccountStoreDirectory() + this.path.sep + source_ssid
|
||||
var new_account = this.getUserStoreDirectory(true);
|
||||
this.fs.cpSync(old_account, new_account, {
|
||||
filter: (source, _destination) => {
|
||||
return source != "pending_transfer.json";
|
||||
},
|
||||
recursive: true
|
||||
});
|
||||
this.fs.rmSync(old_account, { recursive: true })
|
||||
this.fs.rmSync(pending_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
hasPendingTransfer(dtype = null) {
|
||||
var pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||
if (this.fs.existsSync(pending_file)) {
|
||||
var ssidobj = JSON.parse(this.fs.readFileSync(pending_file));
|
||||
console.log(ssidobj)
|
||||
if (dtype) {
|
||||
(ssidobj.type == dtype) ? ssidobj.ssid : false;
|
||||
}
|
||||
else {
|
||||
return ssidobj;
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a file in the user's file store
|
||||
* @param {string} path Relative path to User's file store
|
||||
@@ -631,14 +692,14 @@ class WTVClientSessionData {
|
||||
}
|
||||
|
||||
setSessionData(key, value) {
|
||||
if (key === null) throw ("ClientSessionData.set(): invalid key provided");
|
||||
if (key === null) throw ("ClientSessionData.setSessionData(): invalid key provided");
|
||||
if (typeof (this.session_store) === 'undefined') this.session_store = new Array();
|
||||
this.session_store[key] = value;
|
||||
this.SaveIfRegistered();
|
||||
}
|
||||
|
||||
deleteSessionData(key) {
|
||||
if (key === null) throw ("ClientSessionData.delete(): invalid key provided");
|
||||
if (key === null) throw ("ClientSessionData.deleteSessionData(): invalid key provided");
|
||||
delete this.session_store[key];
|
||||
this.SaveIfRegistered(true);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,19 @@ class WTVRegister {
|
||||
return regex.test(username);
|
||||
}
|
||||
|
||||
checkSSIDAvailable(ssid) {
|
||||
var directory = (directory) ? directory : this.session_store_dir + this.path.sep + "accounts";
|
||||
var available = true;
|
||||
if (this.fs.existsSync(directory)) {
|
||||
this.fs.readdirSync(directory).forEach(file => {
|
||||
if (file.toLowerCase() == ssid.toLowerCase()) {
|
||||
available = false;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
return available;
|
||||
}
|
||||
|
||||
checkUsernameAvailable(username, directory = null) {
|
||||
var self = this;
|
||||
|
||||
@@ -33,8 +33,7 @@ class WTVShared {
|
||||
String.prototype.reverse = function () {
|
||||
var splitString = this.split("");
|
||||
var reverseArray = splitString.reverse();
|
||||
var joinArray = reverseArray.join("");
|
||||
return joinArray;
|
||||
return reverseArray.join("");
|
||||
}
|
||||
}
|
||||
if (!String.prototype.hexEncode) {
|
||||
@@ -80,11 +79,10 @@ class WTVShared {
|
||||
|
||||
|
||||
parseConfigVars(s) {
|
||||
if (s.indexOf("%ServiceDeps%") >= 0) {
|
||||
if (s.indexOf("%ServiceDeps%") >= 0)
|
||||
return this.getServiceDep(s.replace("%ServiceDeps%", ""), true);
|
||||
} else {
|
||||
else
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,12 +117,14 @@ class WTVShared {
|
||||
return new RegExp(src);
|
||||
} else if (src instanceof Date) {
|
||||
return new Date(src.getTime());
|
||||
} else if (Array.isArray(src)) {
|
||||
return src.map(item => this.cloneObj(item));
|
||||
} else if (typeof src === 'object' && src !== null) {
|
||||
const clone = {};
|
||||
Object.keys(src).forEach(k => {
|
||||
clone[k] = this.cloneObj(src[k]);
|
||||
var clone = null;
|
||||
if (Array.isArray(src)) clone = [];
|
||||
else clone = {};
|
||||
|
||||
var self = this;
|
||||
Object.keys(src).forEach((k) => {
|
||||
clone[k] = self.cloneObj(src[k]);
|
||||
});
|
||||
return clone;
|
||||
}
|
||||
@@ -349,7 +349,7 @@ class WTVShared {
|
||||
|
||||
utf8Decode(utf8String) {
|
||||
if (typeof utf8String !== 'string') {
|
||||
throw new TypeError('parameter <EFBFBD>utf8String<EFBFBD> is not a string');
|
||||
throw new TypeError("parameter 'utf8String' is not a string");
|
||||
}
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
const bytes = new Uint8Array(utf8String.split('').map(c => c.charCodeAt(0)));
|
||||
@@ -617,7 +617,7 @@ class WTVShared {
|
||||
* @returns {string} Random string
|
||||
*/
|
||||
generatePassword(len, simple = false) {
|
||||
return this.generateString(len, (simple) ? null : '!@#$%&()[]-_+=?.');
|
||||
return this.generateString(len, (simple) ? '' : '!@#$%&()[]-_+=?.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -749,7 +749,7 @@ class WTVShared {
|
||||
*/
|
||||
filterSSID(obj) {
|
||||
var new_obj = false;
|
||||
if (this.minisrv_config && this.minisrv_config.config.hide_ssid_in_logs) {
|
||||
if (this.minisrv_config.config.hide_ssid_in_logs) {
|
||||
if (typeof obj === "string") {
|
||||
return this.censorSSID(obj);
|
||||
} else if (typeof obj === "object" && obj !== null) {
|
||||
@@ -765,23 +765,18 @@ class WTVShared {
|
||||
|
||||
|
||||
filterRequestLog(obj) {
|
||||
if (this.minisrv_config.config.filter_passwords_in_logs && obj.query) {
|
||||
const passwordRegex = /(^pass$|passw(or)?d)/i;
|
||||
let newobj = this.cloneObj(obj); // Clone the object once at the beginning
|
||||
const passwordRegex = /(^pass$|passw(or)?d)/i;
|
||||
var newobj = this.cloneObj(obj); // Clone the object once at the beginning
|
||||
|
||||
if (newobj.query) {
|
||||
Object.keys(newobj.query).forEach((k) => {
|
||||
if (passwordRegex.test(k)) {
|
||||
newobj.query[k] = '*'.repeat(newobj.query[k].length);
|
||||
}
|
||||
});
|
||||
}
|
||||
delete newobj.raw_headers;
|
||||
|
||||
return newobj;
|
||||
if (newobj.query) {
|
||||
Object.keys(newobj.query).forEach((k) => {
|
||||
if (passwordRegex.test(k)) {
|
||||
newobj.query[k] = '*'.repeat(newobj.query[k].length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return obj;
|
||||
delete newobj.raw_headers;
|
||||
return newobj;
|
||||
}
|
||||
|
||||
|
||||
@@ -974,6 +969,11 @@ class WTVShared {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a 302 redirect and returns the headers/data
|
||||
* @param {any} url
|
||||
* @returns [headers, data]
|
||||
*/
|
||||
doRedirect(url) {
|
||||
var headers = []
|
||||
headers['Status'] = "302 Moved";
|
||||
@@ -984,14 +984,14 @@ class WTVShared {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an error message and sends it to the client
|
||||
* Creates an error message and returns the headers/data
|
||||
* @param {number} code HTTP Error Code
|
||||
* @param {string} data Optinal Custom Error Message
|
||||
* @param {string} details Optional extra error information
|
||||
* @param {boolean} pc_mode If true, sends response formatted for PCs instead of WebTV
|
||||
* @param {boolean} wtv_reset if true, tells the WebTV box to reset the service list and reconnect
|
||||
*/
|
||||
doErrorPage(code, data = null, details = null, pc_mode = false, wtv_reset = false) {
|
||||
doErrorPage(code, data = null, details = null, pc_mode = false, wtv_reset = false) {
|
||||
const minisrv_config = this.minisrv_config;
|
||||
const errorMessage = minisrv_config.config.errorMessages[code] || "";
|
||||
const message = data || errorMessage.replace(/\$\{(\w+)\}/g, (match, p1) => minisrv_config.config[p1] || '');
|
||||
@@ -1000,7 +1000,7 @@ class WTVShared {
|
||||
data += "<br>Details:<br>" + details;
|
||||
}
|
||||
|
||||
let headers = `${code} ${message}\n`;
|
||||
let headers = `Status: ${(pc_mode) ? 'HTTP/1.1' : ''} ${code} ${message}\n`;
|
||||
headers += "Content-Type: text/html\n";
|
||||
|
||||
if (wtv_reset && !pc_mode) {
|
||||
@@ -1075,12 +1075,16 @@ class WTVShared {
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses data and converts it to a base64 string
|
||||
* @param {any} data
|
||||
* @returns {string} base64 string
|
||||
*/
|
||||
packCompressedB64(data) {
|
||||
return this.zlib.deflateSync(data, { 'level': 9 }).toString('base64');
|
||||
}
|
||||
@@ -1176,6 +1180,12 @@ class WTVShared {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a key in an object regardless of its case
|
||||
* @param {string} key Key to find
|
||||
* @param {obj} obj Object to search
|
||||
* @returns {string|null} The found key or null if not found
|
||||
*/
|
||||
getCaseInsensitiveKey(key, obj) {
|
||||
const foundKey = Object.keys(obj).find(k => k.toLowerCase() === key.toLowerCase());
|
||||
return foundKey || null;
|
||||
@@ -1191,6 +1201,17 @@ class clientShowAlert {
|
||||
noback = null;
|
||||
image = null;
|
||||
|
||||
/**
|
||||
* User-friendly client:showalert generation
|
||||
* @param {string} image Image URL
|
||||
* @param {string} message Alert Message (HTML Allowed)
|
||||
* @param {string} buttonlabel1 Button 1 Label
|
||||
* @param {string} buttonaction1 Button 1 Action
|
||||
* @param {string} buttonlabel2 Button 2 Label
|
||||
* @param {string} buttonaction2 Button 2 Action
|
||||
* @param {string} noback If true, disables the back button
|
||||
* @param {string} sound Sound to play
|
||||
*/
|
||||
constructor(image = null, message = null, buttonlabel1 = null, buttonaction1 = null, buttonlabel2 = null, buttonaction2 = null, noback = null, sound = null) {
|
||||
this.message = message;
|
||||
this.buttonlabel1 = buttonlabel1;
|
||||
@@ -1212,6 +1233,10 @@ class clientShowAlert {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client:showalert URL from the helper class
|
||||
* @returns {string} client:showalert URL
|
||||
*/
|
||||
getURL() {
|
||||
var url = "client:ShowAlert?";
|
||||
if (this.message) url += "message=" + escape(this.message) + "&";
|
||||
|
||||
Reference in New Issue
Block a user