From 31c2d94e0b2d0743120e7f30b380c431fe150995 Mon Sep 17 00:00:00 2001 From: zefie Date: Sat, 2 May 2026 15:38:27 -0400 Subject: [PATCH] idfk --- .../headwaiter/connection/boxcheck.html.js | 149 +++-- .../headwaiter/connection/login.aspx.js | 16 +- .../Choose-Your-Profile-Picture.aspx.js | 3 +- .../Establish-your-MSN-TV-Account.html.js | 7 +- .../includes/classes/WTV-MSNTV2.js | 23 +- .../includes/classes/WTVClientSessionData.js | 2 +- .../tools/service_script_check.js | 621 ++++++++++++++++++ 7 files changed, 765 insertions(+), 56 deletions(-) create mode 100644 zefie_wtvp_minisrv/tools/service_script_check.js diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/boxcheck.html.js b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/boxcheck.html.js index 17ac462c..27721b8d 100644 --- a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/boxcheck.html.js +++ b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/boxcheck.html.js @@ -27,6 +27,19 @@ if (!sessionId && !banned) { banned = true; } +if (!session_data && BoxId) { + console.log("Missing session_data for BoxId %s", BoxId); +} + +let registered = false; +let username = ''; +if (session_data) { + registered = session_data.isRegistered(); + if (registered) { + username = session_data.getSessionData("subscriber_username") || ''; + } +} + // Current UTC time const now = new Date(); @@ -58,11 +71,10 @@ const { daylightOffset } = timezoneMap["UTC"]; -ssid_sessions[socket.ssid] = new WTVClientSessionData(minisrv_config, socket.ssid); -ssid_sessions[socket.ssid].set('SessionID', sessionId); - // Set session cookie on the client -setCookie('SessionID', sessionId, { path: '/' }); +if (sessionId) { + setCookie('SessionID', sessionId, { path: '/' }); +} headers = `200 OK Content-type: text/html` @@ -73,9 +85,12 @@ data = ` + + + diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/login.aspx.js b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/login.aspx.js index cccae1c4..48444eda 100644 --- a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/login.aspx.js +++ b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/headwaiter/connection/login.aspx.js @@ -4,10 +4,23 @@ const minisrv_service_file = true; headers = `Content-type: text/html`; +if ( session_data && session_data.isRegistered() ) { data = ` + + + + +`; +} else { + data = ` + + + -`; \ No newline at end of file +`; +} \ No newline at end of file diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Choose-Your-Profile-Picture.aspx.js b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Choose-Your-Profile-Picture.aspx.js index 62752563..774fd651 100644 --- a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Choose-Your-Profile-Picture.aspx.js +++ b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Choose-Your-Profile-Picture.aspx.js @@ -2,13 +2,14 @@ const minisrv_service_file = true; let promoCode = request_headers.query.promo_code || ''; if (Array.isArray(promoCode)) promoCode = promoCode[0]; if (promoCode) { - if (minisrv_config.services['msntv2'] && minisrv_config.services['msntv2'].validPromoCodes && minisrv_config.services['msntv2'].validPromoCodes.includes(promoCode)) { + if (minisrv_config.services[service_name] && minisrv_config.services[service_name].validPromoCodes && minisrv_config.services[service_name].validPromoCodes.includes(promoCode)) { console.log('Valid promo code entered: %s', promoCode); } else { console.warn('Invalid promo code entered: %s', promoCode); promoCode = ''; } setCookie('promo_code', promoCode, { path: '/' }); + session_data.set('promo_code', promoCode); } headers = `Status: 200 OK diff --git a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Establish-your-MSN-TV-Account.html.js b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Establish-your-MSN-TV-Account.html.js index f7726844..95310ba3 100644 --- a/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Establish-your-MSN-TV-Account.html.js +++ b/zefie_wtvp_minisrv/includes/ServiceVault/msntv2/sg1/Register/Establish-your-MSN-TV-Account.html.js @@ -82,7 +82,12 @@ data = ` Bullet I want to sign in as a guest - + + + Bullet + + I want to start over + diff --git a/zefie_wtvp_minisrv/includes/classes/WTV-MSNTV2.js b/zefie_wtvp_minisrv/includes/classes/WTV-MSNTV2.js index 2cd14def..684942b4 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTV-MSNTV2.js +++ b/zefie_wtvp_minisrv/includes/classes/WTV-MSNTV2.js @@ -1447,6 +1447,9 @@ class WTVMSNTV2 { sendLocalFile(socket, filepath, request_headers) { this._populateQuery(request_headers); + if (!request_headers.service_file_path) { + request_headers.service_file_path = filepath; + } try { const ext = path.extname(filepath).slice(1).toLowerCase(); const serviceVaultDir = this.service_config.servicevault_dir || this.service_name; @@ -1466,8 +1469,8 @@ class WTVMSNTV2 { // Resolve socket.ssid from query params before running the script. // Priority: BoxID (direct SSID) > SessionID (looked up in ssid_sessions). if (socket.ssid === null && this.ssid_sessions) { - const boxId = request_headers.query.BoxID || request_headers.query.boxid || null; - const sessionId = request_headers.query.SessionID || request_headers.query.sessionid || null; + const boxId = request_headers.query[this.wtvshared.getCaseInsensitiveKey('boxid', request_headers.query)] || null; + const sessionId = request_headers.query[this.wtvshared.getCaseInsensitiveKey('sessionid', request_headers.query)] || null; if (boxId) { socket.ssid = this.wtvshared.makeSafeSSID(boxId); } else if (sessionId) { @@ -1485,7 +1488,8 @@ class WTVMSNTV2 { } if (socket.ssid && !this.ssid_sessions[socket.ssid]) { this.ssid_sessions[socket.ssid] = new this.WTVClientSessionData(this.minisrv_config, socket.ssid); - this.ssid_sessions[socket.ssid].SaveIfRegistered(); + this.ssid_sessions[socket.ssid].switchUserID(0); + this.ssid_sessions[socket.ssid].loadSessionData(); } } @@ -1500,7 +1504,6 @@ class WTVMSNTV2 { request_is_async: false, session_data: (socket.ssid && this.ssid_sessions) ? this.ssid_sessions[socket.ssid] : null, ssid_sessions: this.ssid_sessions, - WTVClientSessionData: this.WTVClientSessionData, // Scripts may call sendToClient directly for async mode; // wrap it so the response goes through SSLv2 encryption. sendToClient: (sock, hdrs, dat) => self._sendScriptResult(sock, request_headers, hdrs, dat), @@ -1588,6 +1591,10 @@ class WTVMSNTV2 { const responseHeaders = []; responseHeaders.push('HTTP/1.1 200 OK'); responseHeaders.push(`Content-Type: ${contentType}`); + const lastModified = this.wtvshared.getFileLastModifiedUTCString(filepath); + if (lastModified) { + responseHeaders.push(`Last-Modified: ${lastModified}`); + } responseHeaders.push(`Content-Length: ${fileContents.length}`); const closeClientConnection = this._shouldCloseClientConnection(request_headers); responseHeaders.push(`Connection: ${closeClientConnection ? 'close' : 'Keep-Alive'}`); @@ -1663,6 +1670,14 @@ class WTVMSNTV2 { if (!headerLines.some(l => l.toLowerCase().startsWith('content-length'))) { headerLines.push(`Content-Length: ${body.length}`); } + + if (!headerLines.some(l => l.toLowerCase().startsWith('last-modified')) && !headerLines.some(l => l.toLowerCase().startsWith('minisrv-no-last-modified')) && request_headers.service_file_path) { + const lastModified = this.wtvshared.getFileLastModifiedUTCString(request_headers.service_file_path); + if (lastModified) { + headerLines.push(`Last-Modified: ${lastModified}`); + } + } + const closeClientConnection = this._shouldCloseClientConnection(request_headers, headerLines); if (!headerLines.some(l => l.toLowerCase().startsWith('connection:'))) { headerLines.push(`Connection: ${closeClientConnection ? 'close' : 'Keep-Alive'}`); diff --git a/zefie_wtvp_minisrv/includes/classes/WTVClientSessionData.js b/zefie_wtvp_minisrv/includes/classes/WTVClientSessionData.js index acabca81..0dfb4bc2 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTVClientSessionData.js +++ b/zefie_wtvp_minisrv/includes/classes/WTVClientSessionData.js @@ -798,7 +798,7 @@ class WTVClientSessionData { isRegistered(session_mode = true) { if (session_mode) - return (this.getSessionData("registered") && this.fs.existsSync(this.getUserStoreDirectory())); + return Boolean(this.getSessionData("registered") && this.fs.existsSync(this.getUserStoreDirectory())); else return this.fs.existsSync(this.getUserStoreDirectory()); } diff --git a/zefie_wtvp_minisrv/tools/service_script_check.js b/zefie_wtvp_minisrv/tools/service_script_check.js new file mode 100644 index 00000000..caf9eaf7 --- /dev/null +++ b/zefie_wtvp_minisrv/tools/service_script_check.js @@ -0,0 +1,621 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const vm = require('vm'); + +function printUsage() { + console.error('Usage: node tools/service_script_check.js [--line ] [--context ] [--script]'); + console.error('Examples:'); + console.error(' node tools/service_script_check.js boxcheck.html.js --line 83'); + console.error(' node tools/service_script_check.js boxcheck.html.js --script'); + process.exit(1); +} + +const args = process.argv.slice(2); +if (args.length < 2) { + printUsage(); +} + +let filePath; +let lineNumber = null; +let context = 5; +let scriptMode = false; + +for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg === '--line' || arg === '-l') { + i += 1; + lineNumber = args[i] ? Number(args[i]) : NaN; + if (!Number.isInteger(lineNumber) || lineNumber < 1) { + console.error('Error: --line must be a positive integer.'); + process.exit(1); + } + } else if (arg === '--context' || arg === '-c') { + i += 1; + context = args[i] ? Number(args[i]) : NaN; + if (!Number.isInteger(context) || context < 0) { + console.error('Error: --context must be a non-negative integer.'); + process.exit(1); + } + } else if (arg === '--script' || arg === '-s') { + scriptMode = true; + } else if (!filePath) { + filePath = arg; + } else { + console.error(`Unknown argument: ${arg}`); + printUsage(); + } +} + +if (!filePath || (!scriptMode && !lineNumber)) { + printUsage(); +} + +const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath); +if (!fs.existsSync(absolutePath)) { + console.error(`Error: file not found: ${absolutePath}`); + process.exit(1); +} + +const fileText = fs.readFileSync(absolutePath, 'utf8'); + +function findDataLiteral(text) { + const dataPattern = /\bdata\b\s*=\s*/g; + let match; + while ((match = dataPattern.exec(text)) !== null) { + let idx = match.index + match[0].length; + while (idx < text.length && /\s/.test(text[idx])) idx += 1; + if (idx >= text.length) break; + const opener = text[idx]; + if (opener === '`' || opener === '"' || opener === "'") { + return parseLiteral(text, idx); + } + } + return null; +} + +function parseLiteral(text, startIndex) { + const quote = text[startIndex]; + let value = ''; + let i = startIndex + 1; + let escaped = false; + let braceDepth = 0; + let inExpression = false; + + if (quote === '`') { + while (i < text.length) { + const ch = text[i]; + if (escaped) { + value += ch; + escaped = false; + } else if (ch === '\\') { + escaped = true; + value += ch; + } else if (ch === '$' && text[i + 1] === '{') { + inExpression = true; + braceDepth = 0; + value += '${'; + i += 1; + } else if (inExpression) { + if (ch === '{') { + braceDepth += 1; + } else if (ch === '}') { + if (braceDepth === 0) { + inExpression = false; + } else { + braceDepth -= 1; + } + } + value += ch; + } else if (ch === '`') { + return { text: value, startIndex, endIndex: i + 1 }; + } else { + value += ch; + } + i += 1; + } + } else { + while (i < text.length) { + const ch = text[i]; + if (escaped) { + value += ch; + escaped = false; + } else if (ch === '\\') { + escaped = true; + } else if (ch === quote) { + return { text: value, startIndex, endIndex: i + 1 }; + } else { + value += ch; + } + i += 1; + } + } + return null; +} + +function findScriptBlocks(html) { + const pattern = /]*>([\s\S]*?)<\/script>/gi; + const blocks = []; + let match; + while ((match = pattern.exec(html)) !== null) { + const contentOffset = match[0].indexOf(match[1]); + blocks.push({ + content: match[1], + startIndex: match.index, + contentStartIndex: match.index + contentOffset, + raw: match[0] + }); + } + return blocks; +} + +function computeLineNumber(text, index) { + return text.slice(0, index).split(/\r\n|\n|\r/).length + 1; +} + +function printContext(lines, errorLine, context) { + const startLine = Math.max(1, errorLine - context); + const endLine = Math.min(lines.length, errorLine + context); + const width = String(endLine).length; + for (let idx = startLine; idx <= endLine; idx += 1) { + const marker = idx === errorLine ? '>' : ' '; + const padded = String(idx).padStart(width, ' '); + console.log(`${marker} ${padded}: ${lines[idx - 1]}`); + } +} + +function getErrorLocation(err) { + const lineNumber = err.lineNumber || err.line || err.loc?.line || null; + const columnNumber = err.columnNumber || err.column || err.loc?.column || null; + if (lineNumber) { + return { lineNumber, columnNumber }; + } + + if (typeof err.stack === 'string') { + const match = err.stack.match(/\n\s*at .*:(\d+):(\d+)/); + if (match) { + return { lineNumber: Number(match[1]), columnNumber: Number(match[2]) }; + } + } + return { lineNumber: null, columnNumber: null }; +} + +function scanBraceMismatches(text) { + let line = 1; + let column = 1; + let inSingle = false; + let inDouble = false; + let inBacktick = false; + let escaped = false; + let inLineComment = false; + let inBlockComment = false; + let templateExprDepth = 0; + const unmatchedCloses = []; + const openStack = []; + + for (let i = 0; i < text.length; i += 1) { + const ch = text[i]; + const next = text[i + 1]; + + if (inLineComment) { + if (ch === '\n') { + inLineComment = false; + line += 1; + column = 1; + } else { + column += 1; + } + continue; + } + + if (inBlockComment) { + if (ch === '*' && next === '/') { + inBlockComment = false; + i += 1; + column += 2; + continue; + } + if (ch === '\n') { + line += 1; + column = 1; + } else { + column += 1; + } + continue; + } + + if (inSingle) { + if (escaped) { + escaped = false; + } else if (ch === '\\') { + escaped = true; + } else if (ch === "'") { + inSingle = false; + } + if (ch === '\n') { + line += 1; + column = 1; + } else { + column += 1; + } + continue; + } + + if (inDouble) { + if (escaped) { + escaped = false; + } else if (ch === '\\') { + escaped = true; + } else if (ch === '"') { + inDouble = false; + } + if (ch === '\n') { + line += 1; + column = 1; + } else { + column += 1; + } + continue; + } + + if (inBacktick) { + if (escaped) { + escaped = false; + } else if (ch === '\\') { + escaped = true; + } else if (ch === '$' && next === '{') { + templateExprDepth += 1; + i += 1; + column += 2; + continue; + } else if (ch === '`' && templateExprDepth === 0) { + inBacktick = false; + } else if (ch === '}' && templateExprDepth > 0) { + templateExprDepth -= 1; + } + if (ch === '\n') { + line += 1; + column = 1; + } else { + column += 1; + } + continue; + } + + if (ch === '/' && next === '/') { + inLineComment = true; + i += 1; + column += 2; + continue; + } + + if (ch === '/' && next === '*') { + inBlockComment = true; + i += 1; + column += 2; + continue; + } + + if (ch === "'") { + inSingle = true; + column += 1; + continue; + } + + if (ch === '"') { + inDouble = true; + column += 1; + continue; + } + + if (ch === '`') { + inBacktick = true; + templateExprDepth = 0; + column += 1; + continue; + } + + if (ch === '{') { + if (!inBacktick || templateExprDepth > 0) { + openStack.push({ line, column }); + } + column += 1; + continue; + } + + if (ch === '}') { + if (!inBacktick || templateExprDepth > 0) { + if (openStack.length === 0) { + unmatchedCloses.push({ line, column }); + } else { + openStack.pop(); + } + } + column += 1; + continue; + } + + if (ch === '\n') { + line += 1; + column = 1; + } else { + column += 1; + } + } + + return { unmatchedCloses, unmatchedOpens: openStack }; +} + +function findUnmatchedCloseBrace(text) { + const result = scanBraceMismatches(text); + if (result.unmatchedCloses.length > 0) { + return result.unmatchedCloses[0].line; + } + return null; +} + +function findLikelyExtraCloseBrace(text, errorLine) { + const result = scanBraceMismatches(text); + if (result.unmatchedCloses.length > 0) { + return result.unmatchedCloses[0].line; + } + if (result.unmatchedOpens.length > 0) { + // Unclosed open brace—return the last remaining open brace before the end. + return result.unmatchedOpens[result.unmatchedOpens.length - 1].line; + } + return null; +} + +function findLikelyExtraCloseBrace(text, errorLine) { + let line = 1; + let inSingle = false; + let inDouble = false; + let inBacktick = false; + let escaped = false; + let inLineComment = false; + let inBlockComment = false; + let templateExprDepth = 0; + const positions = []; + + for (let i = 0; i < text.length; i += 1) { + const ch = text[i]; + const next = text[i + 1]; + + if (inLineComment) { + if (ch === '\n') { + inLineComment = false; + line += 1; + } + continue; + } + + if (inBlockComment) { + if (ch === '*' && next === '/') { + inBlockComment = false; + i += 1; + continue; + } + if (ch === '\n') { + line += 1; + } + continue; + } + + if (inSingle) { + if (escaped) { + escaped = false; + } else if (ch === '\\') { + escaped = true; + } else if (ch === "'") { + inSingle = false; + } + if (ch === '\n') { + line += 1; + } + continue; + } + + if (inDouble) { + if (escaped) { + escaped = false; + } else if (ch === '\\') { + escaped = true; + } else if (ch === '"') { + inDouble = false; + } + if (ch === '\n') { + line += 1; + } + continue; + } + + if (inBacktick) { + if (escaped) { + escaped = false; + } else if (ch === '\\') { + escaped = true; + } else if (ch === '$' && next === '{') { + templateExprDepth += 1; + i += 1; + continue; + } else if (ch === '`' && templateExprDepth === 0) { + inBacktick = false; + } else if (ch === '}' && templateExprDepth > 0) { + templateExprDepth -= 1; + } + if (ch === '\n') { + line += 1; + } + continue; + } + + if (ch === '/' && next === '/') { + inLineComment = true; + i += 1; + continue; + } + + if (ch === '/' && next === '*') { + inBlockComment = true; + i += 1; + continue; + } + + if (ch === "'") { + inSingle = true; + continue; + } + + if (ch === '"') { + inDouble = true; + continue; + } + + if (ch === '`') { + inBacktick = true; + templateExprDepth = 0; + continue; + } + + if (ch === '}') { + positions.push(line); + } + + if (ch === '\n') { + line += 1; + } + } + + for (let i = positions.length - 1; i >= 0; i -= 1) { + if (positions[i] < errorLine) { + return positions[i]; + } + } + return null; +} + +const literal = findDataLiteral(fileText); +if (!literal) { + console.error('Error: Could not locate a `data` assignment or recognizable string literal in the specified file.'); + process.exit(1); +} + +const dataStartLine = computeLineNumber(fileText, literal.startIndex); +const dataText = literal.text; +const dataLines = dataText.split(/\r\n|\n|\r/); +const totalLines = dataLines.length; + +if (lineNumber !== null) { + if (lineNumber > totalLines) { + console.error(`Error: requested line ${lineNumber} is beyond data content length (${totalLines} lines).`); + process.exit(1); + } + + const startLine = Math.max(1, lineNumber - context); + const endLine = Math.min(totalLines, lineNumber + context); + const width = String(endLine).length; + + console.log(`File: ${absolutePath}`); + console.log(`Data content lines ${startLine}-${endLine} of ${totalLines} (requested ${lineNumber})`); + for (let idx = startLine; idx <= endLine; idx += 1) { + const marker = idx === lineNumber ? '>' : ' '; + const padded = String(idx).padStart(width, ' '); + console.log(`${marker} ${padded}: ${dataLines[idx - 1]}`); + } +} + +if (scriptMode) { + const scriptBlocks = findScriptBlocks(dataText); + if (scriptBlocks.length === 0) { + console.error('Error: no