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/*_*
client_sim_tests
# Large files not pertaining to the service code
UserServiceVault/*-*

View File

@@ -1238,8 +1238,8 @@ function handleProxy(socket, request_type, request_headers, res, data) {
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)
const data_t = Buffer.concat(data).toString().replaceAll("http://", request_type + "://").replaceAll("https://", request_type + "://");
data = [Buffer.from(data_t)]

View File

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

View File

@@ -10,7 +10,7 @@ var photo;
var thumbnail;
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/');
}

View File

@@ -2,7 +2,7 @@ const minisrv_service_file = true;
session_data.loadSessionData();
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];
data = errpage[1];
} else {

View File

@@ -729,19 +729,22 @@ html += `">next page</a>
var pagestorepath = this.pagestore_dir;
var self = this;
self.pageArr = [];
var files = this.fs.readdirSync(pagestorepath)
this.debug("listPages","files",files)
if (self.fs.existsSync(pagestorepath)) {
var files = this.fs.readdirSync(pagestorepath)
this.debug("listPages","files",files)
files.map(function (v) {
// oh yeah it's because any non-JSON file in pagestore will throw an error and break everything
var page_data_raw = null;
var pagepath = pagestorepath + self.path.sep + v;
if (self.fs.existsSync(pagepath)) page_data_raw = self.fs.readFileSync(pagepath);
if (page_data_raw) {
var page_data = JSON.parse(page_data_raw);
self.pageArr.push(page_data);
}
if (v.endsWith(self.pageFileExt)) {
// oh yeah it's because any non-JSON file in pagestore will throw an error and break everything
var page_data_raw = null;
var pagepath = pagestorepath + self.path.sep + v;
if (self.fs.existsSync(pagepath)) page_data_raw = self.fs.readFileSync(pagepath);
if (page_data_raw) {
var page_data = JSON.parse(page_data_raw);
self.pageArr.push(page_data);
}
}
})
}
return self.pageArr;
}
@@ -819,9 +822,14 @@ html += `">next page</a>
var publishname = pagedata.publishname;
if (this.fs.existsSync(destDir + this.wtvclient.session_store.subscriber_username + '/' + publishname)) {
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;
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()
return true;
} catch { }
@@ -881,8 +889,8 @@ html += `">next page</a>
generatePageList() {
// this one's pretty ok i think, but it should have screenshots of each page
var pagelist = this.listPages()
var html = `<HTML>
const pagelist = this.listPages()
let html = `<HTML>
<HEAD>
<SCRIPT language="JavaScript">
var gIsWebTV = false;
@@ -946,19 +954,35 @@ vspace=0
deletePage(pagenum) {
// i hate fs operations
var pagestore = this.pagestoreExists()
var userstore_dir = this.wtvclient.getUserStoreDirectory();
const pagestore = this.pagestoreExists()
const userstore_dir = this.wtvclient.getUserStoreDirectory();
this.debug("deletePage","userstore_dir",userstore_dir)
// PageStore
var store_dir = "PageStore" + this.path.sep;
const store_dir = "PageStore" + this.path.sep;
this.pagestore_dir = userstore_dir + store_dir;
var pagestorepath = this.pagestore_dir;
var page_file = this.fs.readdirSync(pagestorepath)
var page_file_out = page_file[pagenum]
const pagestorepath = this.pagestore_dir;
const page_file = this.fs.readdirSync(pagestorepath)
const page_file_out = page_file[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
createTextBlock(pagenum, title, caption, size, style, position) {
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 (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);
if (this.fs.existsSync(userstore)) {
this.fs.rmSync(userstore, { recursive: true });
@@ -270,7 +273,7 @@ class WTVClientSessionData {
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);
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 null;
@@ -790,6 +793,11 @@ class WTVClientSessionData {
unregisterBox() {
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 {
if (this.fs.existsSync(user_store_base + ".json")) {
this.fs.unlinkSync(user_store_base + ".json");

View File

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

View File

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