remove PB pages when removing/unregistering user

This commit is contained in:
zefie
2025-08-12 14:14:11 -04:00
parent fc3ea3894e
commit 51f2fd5ad7
9 changed files with 74 additions and 46 deletions

View File

@@ -5,6 +5,7 @@
# ServiceLogPost Posted Logs # ServiceLogPost Posted Logs
ServiceLogPost/*_* ServiceLogPost/*_*
client_sim_tests
# Large files not pertaining to the service code # Large files not pertaining to the service code
UserServiceVault/*-* UserServiceVault/*-*

View File

@@ -1238,8 +1238,8 @@ function handleProxy(socket, request_type, request_headers, res, data) {
console.warn(` * HTML transformation failed: ${err.message}`); console.warn(` * HTML transformation failed: ${err.message}`);
} }
} }
if (request_type != "http" && request_type != "https") { if (request_type !== "http" && request_type !== "https") {
// replace http and https links on non http/https protocol (for proto:// for example) // replace http and https links on non http/https protocol (for proto:// for example)
const data_t = Buffer.concat(data).toString().replaceAll("http://", request_type + "://").replaceAll("https://", request_type + "://"); const data_t = Buffer.concat(data).toString().replaceAll("http://", request_type + "://").replaceAll("https://", request_type + "://");
data = [Buffer.from(data_t)] data = [Buffer.from(data_t)]

View File

@@ -1164,7 +1164,7 @@ class WebTVClientSimulator {
} }
// Handle user-id header - indicates successful authentication // Handle user-id header - indicates successful authentication
if (headers['user-id']) { if (headers['user-id'] || (headers['wtv-visit'] && headers['wtv-visit'].startsWith('wtv-register:/splash'))) {
this.debugLog(`*** Authentication successful! user-id detected: ${headers['user-id']} ***`); this.debugLog(`*** Authentication successful! user-id detected: ${headers['user-id']} ***`);
this.userIdDetected = true; this.userIdDetected = true;
@@ -1566,7 +1566,7 @@ class WebTVClientSimulator {
let fileContent = fileResult.body; let fileContent = fileResult.body;
const contentType = fileResult.headers ? fileResult.headers['content-type'] : ''; const contentType = fileResult.headers ? fileResult.headers['content-type'] : '';
const normalizedContentType = contentType.split(';')[0].trim().toLowerCase(); const normalizedContentType = contentType.split(';')[0].trim().toLowerCase();
var isgzip = false; let isgzip = false;
if (normalizedContentType === 'application/gzip' && !this.keepgz) { if (normalizedContentType === 'application/gzip' && !this.keepgz) {
this.debugLog(`Decompressing gzip file for ${fileUrl}...`); this.debugLog(`Decompressing gzip file for ${fileUrl}...`);
@@ -1581,8 +1581,8 @@ class WebTVClientSimulator {
fileContent = fileResult.body; fileContent = fileResult.body;
} }
} }
var filePath = this.getServicePath(fileUrl, fileResult.headers || {}); let filePath = this.getServicePath(fileUrl, fileResult.headers || {});
if (isgzip) filePath = filePath.slice(0, -3); if (isgzip) filePath = filePath.slice(0, -3);
zip.addFile(filePath, fileContent); zip.addFile(filePath, fileContent);
downloadedFiles.add(fileUrl); downloadedFiles.add(fileUrl);
@@ -1800,9 +1800,9 @@ class WebTVClientSimulator {
// Images and media // Images and media
/<img[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi, /<img[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi,
/<image[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi, /<image[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi,
/<video[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi, /<body[^>]+bgsound\s*=\s*["']([^"']+)["'][^>]*>/gi,
/<audio[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi, /<bgsound[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi,
/<source[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi, /<embed[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi,
// Scripts and stylesheets // Scripts and stylesheets
/<script[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi, /<script[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi,
@@ -2366,12 +2366,12 @@ class WebTVClientSimulator {
this.debugLog('Parsing VLN-stage-two HTML for form and hidden fields...'); this.debugLog('Parsing VLN-stage-two HTML for form and hidden fields...');
// Find the form action URL // Find the form action URL
var formMatch = htmlString.match(/<form[^>]+action=["']([^"']+)["'][^>]*>/i); let formMatch = htmlString.match(/<form[^>]+action=["']([^"']+)["'][^>]*>/i);
if (!formMatch) { if (!formMatch) {
// Match <form ... action=... ...> // Match <form ... action=... ...>
// Handles quoted and unquoted action values // Handles quoted and unquoted action values
// Improved regex: match action attribute, quoted or unquoted, non-greedy // Improved regex: match action attribute, quoted or unquoted, non-greedy
var formMatch = htmlString.match(/<form[^>]*\saction\s*=\s*(?:"([^"]*?)"|'([^']*?)'|([^\s"'<>]+))/i); formMatch = htmlString.match(/<form[^>]*\saction\s*=\s*(?:"([^"]*?)"|'([^']*?)'|([^\s"'<>]+))/i);
if (!formMatch) { if (!formMatch) {
console.error('No form with action found in VLN-stage-two HTML'); console.error('No form with action found in VLN-stage-two HTML');
this.cleanup(); this.cleanup();

View File

@@ -10,7 +10,7 @@ var photo;
var thumbnail; var thumbnail;
if (pagedata.blocks[oldBlockNum].photo) { if (pagedata.blocks[oldBlockNum].photo) {
photo = wtvshared.atob(pagedata.blocks[oldBlockNum].photo) photo = wtvshared.btoa(pagedata.blocks[oldBlockNum].photo)
thumbnail = photo.replace('clipart/', 'clipart/icons/'); thumbnail = photo.replace('clipart/', 'clipart/icons/');
} }

View File

@@ -2,7 +2,7 @@ const minisrv_service_file = true;
session_data.loadSessionData(); session_data.loadSessionData();
if (session_data.user_id != 0) { if (session_data.user_id != 0) {
const errpage = doErrorPage(400, "You are not authorized to edit the primary account."); const errpage = wtvshared.doErrorPage(400, "You are not authorized to edit the primary account.");
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} else { } else {

View File

@@ -729,19 +729,22 @@ html += `">next page</a>
var pagestorepath = this.pagestore_dir; var pagestorepath = this.pagestore_dir;
var self = this; var self = this;
self.pageArr = []; self.pageArr = [];
var files = this.fs.readdirSync(pagestorepath) if (self.fs.existsSync(pagestorepath)) {
this.debug("listPages","files",files) var files = this.fs.readdirSync(pagestorepath)
this.debug("listPages","files",files)
files.map(function (v) { files.map(function (v) {
// oh yeah it's because any non-JSON file in pagestore will throw an error and break everything if (v.endsWith(self.pageFileExt)) {
var page_data_raw = null; // oh yeah it's because any non-JSON file in pagestore will throw an error and break everything
var pagepath = pagestorepath + self.path.sep + v; var page_data_raw = null;
if (self.fs.existsSync(pagepath)) page_data_raw = self.fs.readFileSync(pagepath); var pagepath = pagestorepath + self.path.sep + v;
if (page_data_raw) { if (self.fs.existsSync(pagepath)) page_data_raw = self.fs.readFileSync(pagepath);
var page_data = JSON.parse(page_data_raw); if (page_data_raw) {
self.pageArr.push(page_data); var page_data = JSON.parse(page_data_raw);
} self.pageArr.push(page_data);
}
}
}) })
}
return self.pageArr; return self.pageArr;
} }
@@ -819,9 +822,14 @@ html += `">next page</a>
var publishname = pagedata.publishname; var publishname = pagedata.publishname;
if (this.fs.existsSync(destDir + this.wtvclient.session_store.subscriber_username + '/' + publishname)) { if (this.fs.existsSync(destDir + this.wtvclient.session_store.subscriber_username + '/' + publishname)) {
try { try {
this.fs.rmdirSync(destDir + this.wtvclient.session_store.subscriber_username + '/' + publishname, { recursive: true }) this.fs.rmSync(destDir + this.wtvclient.session_store.subscriber_username + '/' + publishname, { recursive: true })
pagedata.published = false; pagedata.published = false;
this.editPage(pagedata, pagenum, false); this.editPage(pagedata, pagenum, false);
const pages = this.listPages()
const publishedPagesCount = pages.filter(page => page.published).length;
if (publishedPagesCount === 0) {
this.fs.rmSync(destDir + this.wtvclient.session_store.subscriber_username, { recursive: true });
}
this.generatePageList() this.generatePageList()
return true; return true;
} catch { } } catch { }
@@ -881,8 +889,8 @@ html += `">next page</a>
generatePageList() { generatePageList() {
// this one's pretty ok i think, but it should have screenshots of each page // this one's pretty ok i think, but it should have screenshots of each page
var pagelist = this.listPages() const pagelist = this.listPages()
var html = `<HTML> let html = `<HTML>
<HEAD> <HEAD>
<SCRIPT language="JavaScript"> <SCRIPT language="JavaScript">
var gIsWebTV = false; var gIsWebTV = false;
@@ -946,19 +954,35 @@ vspace=0
deletePage(pagenum) { deletePage(pagenum) {
// i hate fs operations // i hate fs operations
var pagestore = this.pagestoreExists() const pagestore = this.pagestoreExists()
var userstore_dir = this.wtvclient.getUserStoreDirectory(); const userstore_dir = this.wtvclient.getUserStoreDirectory();
this.debug("deletePage","userstore_dir",userstore_dir) this.debug("deletePage","userstore_dir",userstore_dir)
// PageStore // PageStore
var store_dir = "PageStore" + this.path.sep; const store_dir = "PageStore" + this.path.sep;
this.pagestore_dir = userstore_dir + store_dir; this.pagestore_dir = userstore_dir + store_dir;
var pagestorepath = this.pagestore_dir; const pagestorepath = this.pagestore_dir;
var page_file = this.fs.readdirSync(pagestorepath) const page_file = this.fs.readdirSync(pagestorepath)
var page_file_out = page_file[pagenum] const page_file_out = page_file[pagenum]
this.unpublishPage(pagenum); this.unpublishPage(pagenum);
this.fs.unlinkSync(this.pagestore_dir + page_file_out, { recursive: true }); if (typeof page_file_out === 'undefined') {
this.fs.unlinkSync(this.pagestore_dir + page_file_out, { recursive: true });
}
} }
deleteUser(user_id) {;
this.wtvclient.switchUserID(user_id, false, false, false);
const pagelist = this.listPages();
for (let i = 0; i < pagelist.length; i++) {
this.deletePage(i);
}
const userstore_dir = otherUser.getUserStoreDirectory();
const store_dir = "PageStore" + this.path.sep;
this.pagestore_dir = userstore_dir + store_dir;
this.wtvclient.switchUserID(0, false, false, false);
}
// these totally couldn't have been made into one function nah that's impossible // these totally couldn't have been made into one function nah that's impossible
createTextBlock(pagenum, title, caption, size, style, position) { createTextBlock(pagenum, title, caption, size, style, position) {
var pagedata = this.loadPage(pagenum); var pagedata = this.loadPage(pagenum);

View File

@@ -241,6 +241,9 @@ class WTVClientSessionData {
if (parseInt(this.user_id) !== 0) return false; // not primary account if (parseInt(this.user_id) !== 0) return false; // not primary account
if (user_id === 0) return false; // cannot delete primary account in this fashion if (user_id === 0) return false; // cannot delete primary account in this fashion
const wtvauthor = new WTVAuthor(this.minisrv_config, this)
wtvauthor.deleteUser(user_id);
const userstore = this.getUserStoreDirectory(false, user_id); const userstore = this.getUserStoreDirectory(false, user_id);
if (this.fs.existsSync(userstore)) { if (this.fs.existsSync(userstore)) {
this.fs.rmSync(userstore, { recursive: true }); this.fs.rmSync(userstore, { recursive: true });
@@ -270,7 +273,7 @@ class WTVClientSessionData {
const dest_pending_file = new_userstore + this.path.sep + "pending_transfer.json"; const dest_pending_file = new_userstore + this.path.sep + "pending_transfer.json";
if (this.fs.existsSync(dest_pending_file)) this.fs.unlinkSync(dest_pending_file); if (this.fs.existsSync(dest_pending_file)) this.fs.unlinkSync(dest_pending_file);
this.fs.unlinkSync(pending_file); this.fs.unlinkSync(pending_file);
if (this.fs.existsSync(new_userstore)) this.fs.rmdirSync(new_userstore); if (this.fs.existsSync(new_userstore)) this.fs.rmSync(new_userstore, { recursive: true });
return ssidobj.ssid return ssidobj.ssid
} }
return null; return null;
@@ -790,6 +793,11 @@ class WTVClientSessionData {
unregisterBox() { unregisterBox() {
const user_store_base = this.wtvshared.makeSafePath(this.wtvshared.getAbsolutePath(this.getAccountStoreDirectory()), this.path.sep + this.ssid); const user_store_base = this.wtvshared.makeSafePath(this.wtvshared.getAbsolutePath(this.getAccountStoreDirectory()), this.path.sep + this.ssid);
const accountUsers = this.listPrimaryAccountUsers();
Object.keys(accountUsers).forEach(userKey => {
const user_id = accountUsers[userKey].user_id;
this.pagestore.deleteUser(user_id);
});
try { try {
if (this.fs.existsSync(user_store_base + ".json")) { if (this.fs.existsSync(user_store_base + ".json")) {
this.fs.unlinkSync(user_store_base + ".json"); this.fs.unlinkSync(user_store_base + ".json");

View File

@@ -137,6 +137,7 @@ class WTVDownloadList {
const destination = path.replace("file://", ""); const destination = path.replace("file://", "");
this.putUserStoreDest(path, destination); this.putUserStoreDest(path, destination);
} }
/** /**
* Adds a GET command to the download list * Adds a GET command to the download list
* @param {string} file Non-absolute path of client destination file (relative to group base) * @param {string} file Non-absolute path of client destination file (relative to group base)

View File

@@ -17,12 +17,12 @@ class WTVMinifyingProxy {
]; ];
this.allowedAttributes = [ this.allowedAttributes = [
'leftcolor', 'rightcolor', 'maxlevel', 'leftoffset', 'rightoffset', 'leftcolor', 'rightcolor', 'maxlevel', 'leftoffset', 'rightoffset', 'background',
'host', 'port', 'channel', 'borderimage', 'font', 'nohighlight', 'autoactivate', 'host', 'port', 'channel', 'borderimage', 'font', 'nohighlight', 'autoactivate',
'text', 'cursor', 'text', 'cursor', 'loop', 'autostart', 'href', 'src', 'alt', 'title', 'width',
'href', 'src', 'alt', 'title', 'width', 'height', 'border', 'align', 'valign', 'height', 'border', 'align', 'valign', 'bgcolor', 'color', 'size',
'bgcolor', 'color', 'size', 'face', 'target', 'name', 'value', 'type', 'action', 'face', 'target', 'name', 'value', 'type', 'action',
'method', 'cols', 'rows', 'cellpadding', 'cellspacing', 'nowrap', 'method', 'cols', 'rows', 'cellpadding', 'cellspacing', 'nowrap',
// JellyScript event handlers // JellyScript event handlers
'onclick', 'onload', 'onunload', 'onsubmit', 'onreset', 'onfocus', 'onblur', 'onclick', 'onload', 'onunload', 'onsubmit', 'onreset', 'onfocus', 'onblur',
'onchange', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup' 'onchange', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup'
@@ -495,14 +495,8 @@ class WTVMinifyingProxy {
// Remove other unsupported content // Remove other unsupported content
return html return html
// Remove noscript content (show it since we support basic JS)
.replace(/<noscript\b[^>]*>/gi, '')
.replace(/<\/noscript>/gi, '')
// Remove object/embed tags // Remove object/embed tags
.replace(/<object\b[^>]*>.*?<\/object>/gis, '') .replace(/<object\b[^>]*>.*?<\/object>/gis, '')
.replace(/<embed\b[^>]*\/?>/gi, '')
// Remove iframes
.replace(/<iframe\b[^>]*>.*?<\/iframe>/gis, '')
// Remove link tags (CSS, etc.) // Remove link tags (CSS, etc.)
.replace(/<link\b[^>]*\/?>/gi, '') .replace(/<link\b[^>]*\/?>/gi, '')
// Remove meta tags except content-type and charset // Remove meta tags except content-type and charset