- 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:
zefie
2025-02-12 14:18:32 -05:00
parent c74e2fb71b
commit 81729b66da
55 changed files with 5446 additions and 84056 deletions

View File

@@ -0,0 +1,162 @@
var minisrv_service_file = true;
try {
var relativePath = request_headers.request_url;
if (relativePath.indexOf('?') > -1) relativePath = relativePath.split('?')[0];
while (relativePath.endsWith('/')) relativePath = relativePath.substring(0, relativePath.length - 1);
var dir = service_name + relativePath;
var num_per_page = 25;
var dirs = ['<tr><td>Directory</td><td><a href="' + relativePath + '/..">Parent Directory</a></td><td>-</td><td>-</td></tr > '];
var files = [];
vault_found = false;
// Iterate through each service vault to find the first occurrence
for (let i = 0; i < service_vaults.length; i++) {
const vaultPath = path.join(service_vaults[i], dir || '');
if (!fs.existsSync(vaultPath)) continue;
try {
vault_found = true;
const entries = fs.readdirSync(vaultPath);
entries.forEach(entry => {
if (entry === path.basename(__filename)) return;
// Check if entry exists in all service vaults
let found = false;
var checkPath = "";
for (let j = 0; j < service_vaults.length; j++) {
checkPath = path.join(service_vaults[j], dir || '', entry);
if (fs.existsSync(checkPath)) {
found = true;
break;
}
}
// Skip if not found in all
if (!found) return;
const fullPath = checkPath;
const stats = fs.statSync(fullPath);
const isDir = stats.isDirectory();
const mimeType = (isDir) ? "Directory" : wtvmime.getContentType(fullPath)[1];
var readableSize = '-';
// Get file size with unit
if (!isDir) {
const fileSize = stats.size;
const units = ['Bytes', 'KB', 'MB', 'GB'];
const unitSize = Math.floor(Math.log(fileSize) / Math.log(1024));
readableSize = (fileSize / Math.pow(1024, unitSize)).toFixed(2) + units[unitSize];
}
// Get last modified time
const mtime = stats.mtime.toLocaleString('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
// Check if modified recently
const currentTime = Date.now();
const tenSecondsAgo = currentTime - 10000;
const isModifiedRecently = stats.mtime.getTime() > tenSecondsAgo;
// Create clickable link
const link = isDir ?
`<a href="${relativePath}/${entry}">${entry}</a>` :
`<a href="${relativePath}/${path.basename(fullPath)}">${entry}</a>`;
// Generate icon and timestamp display
const icon = mimeType;
const timestampDisplay = isModifiedRecently ?
`<span style="color: #666;">${mtime}</span>` :
`${mtime}`;
if (isDir) dirs.push(`<tr><td>${icon}</td><td>${(isModifiedRecently) ? entry : link}</td><td>${readableSize}</td><td>${timestampDisplay}</td></tr>`);
else files.push(`<tr><td>${icon}</td><td>${(isModifiedRecently) ? entry : link}</td><td>${readableSize}</td><td>${timestampDisplay}</td></tr>`);
});
} catch (err) {
console.error('Error:', err);
continue;
}
}
if (!vault_found) {
var errpage = wtvshared.doErrorPage(404);
headers = errpage[0];
data = errpage[1];
} else {
// Pagination logic
const totalFiles = files.length;
const max_pages = Math.ceil(totalFiles / num_per_page);
let current_page = 1; // Default to first page
if (totalFiles > 0) {
current_page = parseInt(request_headers.query.page || 1);
if (current_page < 1) current_page = 1;
if (current_page > max_pages) current_page = max_pages;
}
const start_index = (current_page - 1) * num_per_page;
const end_index = current_page * num_per_page;
const merged_files = dirs.concat(files);
const paginatedFiles = merged_files.slice(start_index, end_index);
let paginationHtml = `
<div style="text-align: center; margin-top: 20px;">
<form action="" method="get" style="display: inline;">
<input type=button onclick="window.location.href='?page=1'" ${(current_page === 1) ? 'disabled' : ''} value="First"></input>
<input type=button onclick="window.location.href='?page=${Math.max(current_page - 1, 1)}'" ${(current_page === 1) ? 'disabled' : ''} value="Previous"></input>
<span style="margin: 0 10px;">Page ${current_page} of ${max_pages}</span>
<input type=button onclick="window.location.href='?page=${Math.min(current_page + 1, max_pages)}'" ${(current_page === max_pages) ? 'disabled' : ''} value="Next"></input>
<input type=button onclick="window.location.href='?page=${max_pages}'" ${(current_page === max_pages) ? 'disabled' : ''} value="Last"></input>
</form>
</div>`;
data = `
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Directory Index</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #121212;
color: #ffffff;
}
a, a:hover, a:active {
color: #1e90ff;
text-decoration: none;
}
a:visited { color: #8a2be2; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #333; }
tr:hover { background-color: #2a2a2a; }
</style>
</head>
<body bgcolor="#121212" text="#ffffff" link="#1e90ff" vlink="#8a2be2">
<h2>Directory Index of ${relativePath}</h2>
<hr>
<table>
<tr><th>Type</th><th>Name</th><th>Size</th><th>Last Modified</th></tr>
${paginatedFiles.join('')}
</table>
${paginationHtml}
<script>
function toggleTheme() { ... }
</script>
</body>
</html>`;
headers = `200 OK\nContent-Type: text/html`;
}
} catch (err) {
console.error('Error:', err);
var err = wtvshared.doErrorPage(404);
headers = err[0];
data = err[1];
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,10 @@ var oldBlockNum = request_headers.query.blockNum
var blockType = pagedata.blocks[oldBlockNum].type
var photo;
var thumbnail;
if (pagedata.blocks[oldBlockNum].photo) {
console.log(pagedata.blocks[oldBlockNum].photo);
photo = wtvshared.atob(pagedata.blocks[oldBlockNum].photo)
photo = wtvshared.btoa(pagedata.blocks[oldBlockNum].photo)
thumbnail = photo.replace('clipart/', 'clipart/icons/');
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,281 +1,7 @@
var minisrv_service_file = true;
var create = true;
var pagenums = session_data.pagestore.listPages().length;
if (minisrv_config.services["wtv-author"].max_pages) {
if (pagenums + 1 > minisrv_config.services["wtv-author"].max_pages) {
create = false;
}
}
if (create) {
headers = `200 OK
headers = `302 Moved
Connection: Keep-Alive
Content-Type: text/html`
Location: wtv-author:/styles?tmplClass=11&docName=&styleName=&pageNum=0`;
data = `<HTML>
<HEAD>
<DISPLAY fontsize=medium>
<TITLE>Select a page style</TITLE>
</HEAD>
<sidebar width=122 height=420 align=left>
<table cellspacing=0 cellpadding=0 height=385>
<TR>
<td width=3>
<td abswidth=2 bgColor=#8A99B0 rowspan=99>
<td absHEIGHT=4>&nbsp;
<td abswidth=2 bgColor=#8A99B0 rowspan=99>
<td width=4 rowspan=99>
<td backGround="wtv-author:/ROMCache/grad_tile.gif" width=16 rowspan=99>
</TR>
<tr>
<td>
<td align=right height=69 width=93 href="wtv-home:/home">
<img src="${minisrv_config.config.service_logo}" width=87 height=67>
<tr>
<td absheight=5>&nbsp;
<TR>
<td colspan=5 absheight=2 valign=middle align=center bgcolor=#8A99B0>&nbsp;
<tr>
<td>
<td abswidth=93 absheight=26>
<table href="wtv-author:/documents" cellspacing=0 cellpadding=0>
<tr>
<td abswidth=5>
<td abswidth=90 valign=middle align=left>
<table cellspacing=0 cellpadding=0>
<tr>
<td maxlines=1>
<font sizerange=medium color="C2CCD7">Index</font>
</table>
</table>
<TR>
<td colspan=5 absheight=2 valign=middle align=center bgcolor=#8A99B0>&nbsp;
<tr>
<td>
<td abswidth=93 absheight=26>
<table href="wtv-guide:/help?topic=Glossary&subtopic=P&page=pagebuilder" cellspacing=0 cellpadding=0>
<tr>
<td abswidth=5>
<td abswidth=90 valign=middle align=left>
<table cellspacing=0 cellpadding=0>
<tr>
<td maxlines=1>
<font sizerange=medium color="C2CCD7">Help</font>
</table>
</table>
<TR>
<td colspan=5 absheight=2 valign=middle align=center bgcolor=#8A99B0>&nbsp;
<tr>
<td>
<td valign=bottom align=right>
<img src="wtv-author:/ROMCache/pagebuilder.gif" height=124 width=78>&nbsp;
</table>
</sidebar>
<body bgcolor=#1e4261 background=wtv-author:/ROMCache/blue_tile_128.gif text=AEBFD1 link=B8BDC7 vlink=B8BDC7 hspace=0 vspace=0>
<table cellspacing=0 cellpadding=0 width=100%>
<tr>
<td abswidth=10 rowspan=100>
<tr>
<td height=44 width=206 valign=middle colspan=2>
<font size=+1 color=D1D1D1>
<blackface> Choose a page style </blackface>
</font>
<td align=right valign=middle>
<table valign=middle>
<tr>
<td>
<table cellspacing=0 cellpadding=0>
<tr>
<td>
<img src="wtv-author:/ROMCache/minus_button_dim.gif">
</table>
</td>
<td align=center>
<font color=D1D1D1>
<B>1 of 1</B>
</font>
</td>
<td>
<table cellspacing=0 cellpadding=0 ref="wtv-author:/styles?tmplClass=11&docName=&styleName=&pageNum=1#plus" id=plus>
<tr>
<td>
<img src="wtv-author:/ROMCache/plus_button_dim.gif">
</table>
</td>
</tr>
</table>
<tr>
<td absheight=5>
<tr>
<td colspan=5>
<font color=AEBFD1> You can change the style at any time without losing your text or images. </font>
<tr>
<td absheight=10>
<tr>
<td absheight=5>
<tr>
<td>
<TABLE border=0 cellpadding=0 cellspacing=0 width=137>
<TR height=120>
<TD width=128 height=120 bgcolor=#8A99B0 href="new?tmplClass=&layoutTMPL=Blue_Sands.tmpl&contentTMPL=&styleName=Blue Sands&blocksPerPage=0" name="Blue Sands">
<spacer type=block width=4></spacer>
<font color=0F283F> Blue Sands <BR>
</font>
<center>
<img src=/images/styles/blue_sands.gif width="120" height="90" vspace=3>
</center>
</TD>
<TD height=120>
<IMG height=120 width=12 src=/ROMCache/right_shadow.gif>
</TD>
</TR>
<TR>
<TD width=128>
<IMG height=12 width=128 src=/ROMCache/bottom_shadow.gif>
</TD>
<TD>
<IMG height=12 width=12 src=/ROMCache/corner_shadow.gif>
</TD>
</TR>
</TABLE>
</td>
<td>
<TABLE border=0 cellpadding=0 cellspacing=0 width=137>
<TR height=120>
<TD width=128 height=120 bgcolor=#8A99B0 href="new?tmplClass=&layoutTMPL=Space.tmpl&contentTMPL=&styleName=Space&blocksPerPage=0" name="Space">
<spacer type=block width=4></spacer>
<font color=0F283F> Space <BR>
</font>
<center>
<img src=/images/styles/space.gif width="120" height="90" vspace=3>
</center>
</TD>
<TD height=120>
<IMG height=120 width=12 src=/ROMCache/right_shadow.gif>
</TD>
</TR>
<TR>
<TD width=128>
<IMG height=12 width=128 src=/ROMCache/bottom_shadow.gif>
</TD>
<TD>
<IMG height=12 width=12 src=/ROMCache/corner_shadow.gif>
</TD>
</TR>
</TABLE>
</td>
<td>
<TABLE border=0 cellpadding=0 cellspacing=0 width=137>
<TR height=120>
<TD width=128 height=120 bgcolor=#8A99B0 href="new?tmplClass=&layoutTMPL=Cats.tmpl&contentTMPL=&styleName=Cats&blocksPerPage=0" name="Cats">
<spacer type=block width=4></spacer>
<font color=0F283F> Cats <BR>
</font>
<center>
<img src=/images/styles/catz.gif width="120" height="90" vspace=3>
</center>
</TD>
<TD height=120>
<IMG height=120 width=12 src=/ROMCache/right_shadow.gif>
</TD>
</TR>
<TR>
<TD width=128>
<IMG height=12 width=128 src=/ROMCache/bottom_shadow.gif>
</TD>
<TD>
<IMG height=12 width=12 src=/ROMCache/corner_shadow.gif>
</TD>
</TR>
</TABLE>
</td>
<tr>
<td>
<TABLE border=0 cellpadding=0 cellspacing=0 width=137>
<TR height=120>
<TD width=128 height=120 bgcolor=#8A99B0 href="new?tmplClass=&layoutTMPL=Blue.tmpl&contentTMPL=&styleName=Blue&blocksPerPage=0" name="Blue">
<spacer type=block width=4></spacer>
<font color=0F283F> Blue <BR>
</font>
<center>
<img src=/images/styles/blue.gif width="120" height="90" vspace=3>
</center>
</TD>
<TD height=120>
<IMG height=120 width=12 src=/ROMCache/right_shadow.gif>
</TD>
</TR>
<TR>
<TD width=128>
<IMG height=12 width=128 src=/ROMCache/bottom_shadow.gif>
</TD>
<TD>
<IMG height=12 width=12 src=/ROMCache/corner_shadow.gif>
</TD>
</TR>
</TABLE>
</td>
<td>
<TABLE border=0 cellpadding=0 cellspacing=0 width=137>
<TR height=120>
<TD width=128 height=120 bgcolor=#8A99B0 href="new?tmplClass=&layoutTMPL=Red.tmpl&contentTMPL=&styleName=Red&blocksPerPage=0" name="Red">
<spacer type=block width=4></spacer>
<font color=0F283F> Red <BR>
</font>
<center>
<img src=/images/styles/red.gif width="120" height="90" vspace=3>
</center>
</TD>
<TD height=120>
<IMG height=120 width=12 src=/ROMCache/right_shadow.gif>
</TD>
</TR>
<TR>
<TD width=128>
<IMG height=12 width=128 src=/ROMCache/bottom_shadow.gif>
</TD>
<TD>
<IMG height=12 width=12 src=/ROMCache/corner_shadow.gif>
</TD>
</TR>
</TABLE>
</td>
<td>
<TABLE border=0 cellpadding=0 cellspacing=0 width=137>
<TR height=120>
<TD width=128 height=120 bgcolor=#8A99B0 href="new?tmplClass=&layoutTMPL=Republican.tmpl&contentTMPL=&styleName=Republican&blocksPerPage=0" name="Republican">
<spacer type=block width=4></spacer>
<font color=0F283F> Republican <BR>
</font>
<center>
<img src=/images/styles/republican.gif width="120" height="90" vspace=3>
</center>
</TD>
<TD height=120>
<IMG height=120 width=12 src=/ROMCache/right_shadow.gif>
</TD>
</TR>
<TR>
<TD width=128>
<IMG height=12 width=128 src=/ROMCache/bottom_shadow.gif>
</TD>
<TD>
<IMG height=12 width=12 src=/ROMCache/corner_shadow.gif>
</TD>
</TR>
</TABLE>
</td>
</table>
</BODY>
</HTML>
`;
} else {
var err = wtvshared.doErrorPage(500, "You are not allowed to create more than <b>"+minisrv_config.services["wtv-author"].max_pages+"</b> pages.");
headers = err[0];
data = err[1];
}
data = ''

View File

@@ -4,94 +4,132 @@ var challenge_response, challenge_header = '';
var gourl;
var wtvsec_login = null;
if (request_headers.query.hangup) {
headers = `300 OK
hasPendingTransfer = session_data.hasPendingTransfer()
if (hasPendingTransfer) {
if (hasPendingTransfer.type == "target") {
var xferSession = new WTVClientSessionData(minisrv_config, hasPendingTransfer.ssid);
xferSession.user_id = 0
var primary_username = xferSession.listPrimaryAccountUsers()['subscriber']['subscriber_username'];
var transferPendingDest = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "There is a pending transfer of the account <b>" + primary_username + "</b>, would you like to complete the transfer, or cancel it?",
'buttonlabel1': "Complete Transfer",
'buttonaction1': "wtv-head-waiter:/complete-account-transfer",
'buttonlabel2': "Cancel Transfer",
'buttonaction2': "wtv-head-waiter:/cancel-account-transfer",
'noback': true,
}).getURL();
var errpage = wtvshared.doRedirect(transferPendingDest);
var headers = errpage[0];
var data = errpage[1];
} else if (hasPendingTransfer.type == "source") {
var transferPendingSrc = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "There is a pending transfer of this account to <b>" + hasPendingTransfer.ssid + "</b>. In order to use this box, you need to complete or cancel the transfer.",
'buttonlabel1': "Power Off",
'buttonaction1': "client:poweroff",
'buttonlabel2': "Cancel Transfer",
'buttonaction2': "wtv-head-waiter:/cancel-account-transfer",
'noback': true,
}).getURL();
var errpage = wtvshared.doRedirect(transferPendingSrc);
var headers = errpage[0];
var data = errpage[1];
} else {
console.log(hasPendingTransfer);
}
} else {
if (request_headers.query.hangup) {
headers = `300 OK
Location: client:gototvhome
wtv-visit: client:hangupphone`
} else {
var user_id = (request_headers.query.user_id) ? request_headers.query.user_id : session_data.user_id;
if (socket.ssid !== null && user_id !== null) session_data.switchUserID(user_id);
if (socket.ssid !== null && !session_data.get("wtvsec_login")) {
wtvsec_login = session_data.createWTVSecSession();
wtvsec_login.IssueChallenge();
wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
session_data.set("wtvsec_login", wtvsec_login);
} else {
wtvsec_login = session_data.get("wtvsec_login");
}
var user_id = (request_headers.query.user_id) ? request_headers.query.user_id : session_data.user_id;
if (socket.ssid !== null) {
if (wtvsec_login.ticket_b64 == null) {
challenge_response = wtvsec_login.challenge_response;
var client_challenge_response = request_headers["wtv-challenge-response"] || null;
if (challenge_response && client_challenge_response) {
if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) {
console.log(" * wtv-challenge-response success for " + wtvshared.filterSSID(socket.ssid));
wtvsec_login.PrepareTicket();
gourl = "wtv-head-waiter:/login-stage-two?";
if (socket.ssid !== null && user_id !== null) session_data.switchUserID(user_id);
if (socket.ssid !== null && !session_data.get("wtvsec_login")) {
wtvsec_login = session_data.createWTVSecSession();
wtvsec_login.IssueChallenge();
wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
session_data.set("wtvsec_login", wtvsec_login);
} else {
wtvsec_login = session_data.get("wtvsec_login");
}
if (socket.ssid !== null) {
if (wtvsec_login.ticket_b64 == null) {
challenge_response = wtvsec_login.challenge_response;
var client_challenge_response = request_headers["wtv-challenge-response"] || null;
if (challenge_response && client_challenge_response) {
if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) {
console.log(" * wtv-challenge-response success for " + wtvshared.filterSSID(socket.ssid));
wtvsec_login.PrepareTicket();
gourl = "wtv-head-waiter:/login-stage-two?";
} else {
console.log(" * wtv-challenge-response FAILED for " + wtvshared.filterSSID(socket.ssid));
if (minisrv_config.config.debug_flags.debug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64));
if (minisrv_config.config.debug_flags.debug) console.log("Response Received:", client_challenge_response)
gourl = "wtv-head-waiter:/login?reissue_challenge=true";
}
} else {
console.log(" * wtv-challenge-response FAILED for " + wtvshared.filterSSID(socket.ssid));
if (minisrv_config.config.debug_flags.debug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64));
if (minisrv_config.config.debug_flags.debug) console.log("Response Received:", client_challenge_response)
gourl = "wtv-head-waiter:/login?reissue_challenge=true";
gourl = "wtv-head-waiter:/login?no_response=true";
}
} else {
gourl = "wtv-head-waiter:/login?no_response=true";
gourl = "wtv-head-waiter:/login-stage-two?";
}
} else {
gourl = "wtv-head-waiter:/login-stage-two?";
}
}
if (request_headers.query.guest_login) {
if (request_headers.query.relogin || request_headers.query.reconnect) gourl += "&";
gourl += "guest_login=true";
if (request_headers.query.skip_splash) gourl += "&skip_splash=true";
}
if (request_headers.query.guest_login) {
if (request_headers.query.relogin || request_headers.query.reconnect) gourl += "&";
gourl += "guest_login=true";
if (request_headers.query.skip_splash) gourl += "&skip_splash=true";
}
if (user_id != null && !request_headers.query.initial_login && !request_headers.query.user_login && !request_headers.query.relogin && !request_headers.query.reconnect) {
if (request_headers.query.password == "") {
headers = `403 Please enter your password and try again
if (user_id != null && !request_headers.query.initial_login && !request_headers.query.user_login && !request_headers.query.relogin && !request_headers.query.reconnect) {
if (request_headers.query.password == "") {
headers = `403 Please enter your password and try again
minisrv-no-mail-count: true
`;
} else if (session_data.validateUserPassword(request_headers.query.password)) {
session_data.setUserLoggedIn(true);
headers = `200 OK
} else if (session_data.validateUserPassword(request_headers.query.password)) {
session_data.setUserLoggedIn(true);
headers = `200 OK
minisrv-no-mail-count: true
Content-Type: text/html
wtv-visit: ${gourl}
`;
} else {
headers = `403 The password you entered was incorrect. Please retype it and try again.
} else {
headers = `403 The password you entered was incorrect. Please retype it and try again.
minisrv-no-mail-count: true
`;
}
} else {
if (session_data.baddisk === true && !ssid_sessions[socket.ssid].get("bad_disk_shown")) {
gourl = "wtv-head-waiter:/bad-disk?"
}
else if (session_data.getNumberOfUserAccounts() > 1 && user_id === 0 && (!session_data.isUserLoggedIn() || request_headers.query.initial_login || request_headers.query.relogin) ) {
gourl = "wtv-head-waiter:/choose-user?"
}
} else {
if (!session_data.getUserPasswordEnabled() && request_headers.query.user_login) session_data.setUserLoggedIn(true);
var limitedLogin = (!session_data.lockdown && (!session_data.get('password_valid') && session_data.getUserPasswordEnabled()));
var limitedLoginRegistered = (limitedLogin && session_data.isRegistered());
}
headers = `200 OK
if (session_data.baddisk === true && !ssid_sessions[socket.ssid].get("bad_disk_shown")) {
gourl = "wtv-head-waiter:/bad-disk?"
}
else if (session_data.getNumberOfUserAccounts() > 1 && user_id === 0 && (!session_data.isUserLoggedIn() || request_headers.query.initial_login || request_headers.query.relogin)) {
gourl = "wtv-head-waiter:/choose-user?"
} else {
if (!session_data.getUserPasswordEnabled() && request_headers.query.user_login) session_data.setUserLoggedIn(true);
var limitedLogin = (!session_data.lockdown && (!session_data.get('password_valid') && session_data.getUserPasswordEnabled()));
var limitedLoginRegistered = (limitedLogin && session_data.isRegistered());
}
headers = `200 OK
wtv-connection-close: true
Connection: close
minisrv-no-mail-count: true
Content-Type: text/html`;
if (client_challenge_response) {
headers += `
if (client_challenge_response) {
headers += `
wtv-encrypted: ${(request_headers['wtv-encrypted']) ? wtvshared.parseBool(request_headers['wtv-encrypted']) : true}`;
if (wtvsec_login) session_data.data_store.wtvsec_login.update_ticket = true;
}
if (limitedLoginRegistered && session_data.getUserPasswordEnabled()) gourl = "wtv-head-waiter:/password?";
headers += `
if (wtvsec_login) session_data.data_store.wtvsec_login.update_ticket = true;
}
if (limitedLoginRegistered && session_data.getUserPasswordEnabled()) gourl = "wtv-head-waiter:/password?";
headers += `
wtv-visit: ${gourl}`;
}
}
}

View File

@@ -0,0 +1,13 @@
var minisrv_service_file = true;
var ssid = session_data.cancelPendingTransfer();
var transferCanceled = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "The transfer of this account to <b>" + ssid + "</b> has been cancelled.",
'buttonlabel1': "Okay",
'buttonaction1': "wtv-head-waiter:/login",
'noback': true,
}).getURL();
var errpage = wtvshared.doRedirect(transferCanceled);
var headers = errpage[0];
var data = errpage[1];

View File

@@ -0,0 +1,24 @@
var minisrv_service_file = true;
var result = session_data.finalizePendingTransfer();
if (result) {
var transferCanceled = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "The transfer is complete.",
'buttonlabel1': "Login",
'buttonaction1': "wtv-head-waiter:/login",
'noback': true,
}).getURL();
} else {
var transferCanceled = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "The transfer failed.",
'buttonlabel1': "Try to Login",
'buttonaction1': "wtv-head-waiter:/login",
'noback': true,
}).getURL();
}
var errpage = wtvshared.doRedirect(transferCanceled);
var headers = errpage[0];
var data = errpage[1];

View File

@@ -3,7 +3,6 @@ session_data.setUserLoggedIn(false);
var challenge_response, challenge_header = "";
if (socket.ssid !== null) session_data.switchUserID(0);
var gourl = "wtv-head-waiter:/ValidateLogin?";
if (request_headers.query.relogin) gourl += "relogin=true";
else if (request_headers.query.reconnect) gourl += "reconnect=true";

View File

@@ -0,0 +1,146 @@
var minisrv_service_file = true;
// security
if (session_data.user_id != 0 && session_data.user_id != request_headers.query.user_id) {
var errpage = wtvshared.doErrorPage(400, "You are not authorized to transfer this account. Please log in as the primary user.");
headers = errpage[0];
data = errpage[1];
}
if (!session_data.getUserPasswordEnabled()) {
var passwordRequired = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "For security, you must first set a password on your account before you can transfer it.",
'buttonlabel1': "Set Password",
'buttonaction1': "wtv-setup:/edit-password",
'buttonlabel2': "Cancel",
'buttonaction2': "client:donothing",
'noback': true,
}).getURL();
var errpage = wtvshared.doRedirect(passwordRequired);
headers = errpage[0];
data = errpage[1];
} else if (session_data.getUserPasswordEnabled() && session_data.user_id === 0) {
headers = `200 OK
Connection: Keep-Alive
Content-Type: text/html
wtv-expire-all: wtv-setup:/transfer-account
wtv-noback-all: wtv-setup:/transfer-account`;
data = `<HTML>
<HEAD>
<TITLE>
Transfer your account
</TITLE>
<DISPLAY nosave skipback noscroll>
</HEAD>
<sidebar width=110> <table cellspacing=0 cellpadding=0 BGCOLOR="30364D">
<tr>
<td colspan=3 abswidth=104 absheight=4>
<td rowspan=99 width=6 absheight=420 valign=top align=left>
<img src="wtv-home:/ROMCache/Shadow.gif" width=6 height=420>
<tr>
<td abswidth=6>
<td abswidth=92 absheight=76>
<table href="wtv-home:/home" absheight=76 cellspacing=0 cellpadding=0>
<tr>
<td align=right>
<img src="${minisrv_config.config.service_logo}" width=87 height=67>
</table>
<td abswidth=6>
<tr><td absheight=5 colspan=3>
<table cellspacing=0 cellpadding=0>
<tr><td abswidth=104 absheight=2 valign=middle align=center bgcolor="1C1E28">
<img src="wtv-home:/ROMCache/Spacer.gif" width=1 height=1>
<tr><td abswidth=104 absheight=1 valign=top align=left>
<tr><td abswidth=104 absheight=2 valign=top align=left bgcolor="4D5573">
<img src="wtv-home:/ROMCache/Spacer.gif" width=1 height=1>
</table>
<tr><td absheight=37>
<tr><td absheight=263 align=right colspan=3>
<img src="ROMCache/AccountBanner.gif" width=53 height=263>
<tr><td absheight=41>
</table>
</sidebar>
<BODY BGCOLOR="#191919" TEXT="#44cc55" LINK="189CD6" VLINK="189CD6" HSPACE=0 VSPACE=0 FONTSIZE="large"
>
<table cellspacing=0 cellpadding=0>
<tr>
<td abswidth=14>
<td abswidth=416 absheight=80 valign=center>
<font size="+2" color="E7CE4A"><blackface><shadow>
Transfer your account
<td abswidth=20>
<tr>
<td>
<td absheight=244 valign=top align=left>
<form
action="wtv-setup:/validate-transfer-account"
>
<P>
<table cellspacing=0 cellpadding=0 border=0>
<tr>
<td align=left valign=top abswidth=400 colspan=2>
<table cellspacing=0 cellpadding=0>
<tr>
<td align=left>
Enter the SSID of the target box, and the current primary user's password.
</table>
<tr>
<tr>
</td>
<td align=left valign=top width=6>
</td>
<table cellspacing=0 cellpadding=0>
<tr><td absheight=15></td></tr>
<tr>
<td colspan=3 align=left>
SSID<br>
<INPUT noSubmit name="ssid" id="ssid" Value=""
bgcolor=#444444 text=#ffdd33 cursor=#cc9933
TYPE="text" ASCIIONLY
SIZE="20"
MAXLENGTH="16">
<tr>
<td height=6>
<tr>
<td colspan=3 align=left>
<br>Primary User Password<br>
<INPUT noSubmit name="password" id="password" Value=""
bgcolor=#444444 text=#ffdd33 cursor=#cc9933
TYPE="password" ASCIIONLY
SIZE="20"
MAXLENGTH="${minisrv_config.config.passwords.max_length}">
</a>
</table>
</table>
<td>
<tr>
<td absheight=7>
<tr>
<td>
<td colspan=2 absheight=2 bgcolor="2B2B2B">
<img src="wtv-home:/ROMCache/Spacer.gif" width=426 height=1>
<tr>
<td absheight=1>
<tr>
<td>
<td colspan=2 absheight=2 bgcolor="0D0D0D">
<img src="wtv-home:/ROMCache/Spacer.gif" width=426 height=1>
<tr>
<td absheight=4>
</table>
<table cellspacing=0 cellpadding=0>
<tr>
<td abswidth=430 valign=top align=right>
<font color="#E7CE4A" size=-1><shadow>
<input
selected
type=submit borderimage="file://ROM/Borders/ButtonBorder2.bif"
value=Continue name="Continue" usestyle width=103>
</shadow></font></form>
<td abswidth=20>
</table>
</body>
</html>`
}

View File

@@ -0,0 +1,69 @@
var minisrv_service_file = true;
var wtvr = new WTVRegister(minisrv_config);
// security
if (session_data.user_id != 0 && session_data.user_id != request_headers.query.user_id) {
var errpage = wtvshared.doErrorPage(400, "You are not authorized to transfer this account. Please log in as the primary user.");
headers = errpage[0];
data = errpage[1];
}
if (!request_headers.query.ssid || !request_headers.query.password) {
var errpage = wtvshared.doErrorPage(400, "Invalid Parameter");
headers = errpage[0];
data = errpage[1];
}
if (!session_data.getUserPasswordEnabled()) {
var passwordRequired = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "For security, you must first set a password on your account before you can transfer it.",
'buttonlabel1': "Set Password",
'buttonaction1': "wtv-setup:/edit-password",
'buttonlabel2': "Cancel",
'buttonaction2': "client:donothing",
'noback': true,
}).getURL();
var errpage = wtvshared.doRedirect(passwordRequired);
headers = errpage[0];
data = errpage[1];
} else if (session_data.getUserPasswordEnabled() && session_data.user_id === 0 && request_headers.query.ssid && request_headers.query.password) {
validPassword = session_data.validateUserPassword(request_headers.query.password);
if (!validPassword) {
var errpage = wtvshared.doErrorPage(400, "Incorrect Password");
headers = errpage[0];
data = errpage[1];
}
else if (!wtvshared.checkSSID(request_headers.query.ssid)) {
var errpage = wtvshared.doErrorPage(400, "The provided SSID is not valid. Only valid CRC validated SSIDs are available as a destination. Please check your input and try again.");
headers = errpage[0];
data = errpage[1];
}
else if (!wtvr.checkSSIDAvailable(request_headers.query.ssid)) {
var errpage = wtvshared.doErrorPage(400, "The destination already has an account registered, or a transfer is already in progress. Please delete the account associated with the target SSID, or cancel the pending transfer, then try again.");
headers = errpage[0];
data = errpage[1];
} else {
var transferInitiated = new clientShowAlert({
'image': minisrv_config.config.service_logo,
'message': "Your account transfer is pending. Please connect to this server with the destination box. A prompt should appear instead of registration. To cancel the transfer, select <b>Cancel Transfer</b>, or simply reconnect with this box.",
'buttonlabel1': "Cancel Transfer",
'buttonaction1': "wtv-head-waiter:/cancel-account-transfer",
'buttonlabel2': "Power Off",
'buttonaction2': "client:poweroff",
'noback': true,
}).getURL();
session_data.setPendingTransfer(request_headers.query.ssid);
session_data.setUserLoggedIn(false);
var errpage = wtvshared.doRedirect(transferInitiated);
headers = errpage[0];
data = errpage[1];
}
} else {
var errpage = wtvshared.doErrorPage(400, "Invalid Parameter");
headers = errpage[0];
data = errpage[1];
}

View File

@@ -16,12 +16,17 @@ function getLegend() {
headers = `200 OK
Content-Type: text/html`;
data = `<html><head><display allowoffline switchtowebmode>
<script src=/ROMCache/h.js></script><script src=/ROMCache/n.js></script>
<script src=htv-cSetup.js></script>
</head ><form name=t><input type=hidden name=h value=&pname;></form>
<script>head('WebTV Character Map');</script>
<br><br>
data = `<html>
<body>
<display nosave nosend>
<title>Character Map</title>
<sidebar width=20%>
<img src="wtv-tricks:/images/Favorites_bg.jpg">
</sidebar>
<body bgcolor="#191919" text="#44cc55" link="36d5ff" vlink="36d5ff" vspace=0>
<br>
<br>
<h1>WebTV Character Map</h1>
<table border=1>`;

View File

@@ -1,131 +1,283 @@
var minisrv_service_file = true;
var client_caps = null;
let client_caps = null;
if (socket.ssid != null) {
if (session_data.capabilities) {
client_caps = session_data.capabilities;
}
if (session_data.capabilities) {
client_caps = session_data.capabilities;
}
}
if (client_caps) {
headers = `200 OK
Content-Type: text/html`
var service_ip = minisrv_config.config.service_ip
var client_label = "TODO";
var boot_client_label = "TODO";
var wtv_system_sysconfig_str = "TODO";
headers = `200 OK
Content-Type: text/html`;
const versionMap = [
{ build: 0, vers: `1.0` },
{ build: 200, vers: `1.1` },
{ build: 300, vers: `1.2` },
{ build: 1000, vers: `1.3` },
{ build: 1090, vers: `1.3Retail` },
{ build: 1127, vers: `1.4Retail` },
{ build: 1150, vers: `1.4` },
{ build: 2000, vers: `2.0` },
{ build: 2100, vers: `2.0J` },
{ build: 2150, vers: `2.0.1J` },
{ build: 2200, vers: `2.0.1` },
{ build: 2300, vers: `2.0.3` },
{ build: 2500, vers: `2.0.5` },
{ build: 3000, vers: `2.1` },
{ build: 3065, vers: `2.1.1` },
{ build: 3070, vers: `2.1.5` },
{ build: 3250, vers: `2.1.7` },
{ build: 3450, vers: `Springboard2.2` },
{ build: 3600, vers: `2.0.2J` },
{ build: 3700, vers: `2.2.1J` },
{ build: 3800, vers: `2.2.5` },
{ build: 5000, vers: `2.3` },
{ build: 5200, vers: `Fiji` },
{ build: 5500, vers: `2.3.5` },
{ build: 5700, vers: `2.3.7` },
{ build: 5750, vers: `2.3.8` },
{ build: 5759, vers: `2.3.8-NAND` },
{ build: 6000, vers: `3.0` }, // WNI actually did this, i'm pretty sure they gave up
{ build: 32767, vers: `Private` },
];
function getVersion(givenBuild) {
return (versionMap.at(versionMap.findIndex(({ build }) => build > givenBuild) - 1).vers);
}
const serviceIP = minisrv_config.config.service_ip;
const zTitle = `WebTV Services, (${minisrv_version_string})`;
const systemVersion = session_data.get("wtv-system-version");
const bootromVersion = session_data.get("wtv-client-bootrom-version");
const SSID = wtvshared.filterSSID(
session_data.get("wtv-client-serial-number")
);
const romType = session_data.get("wtv-client-rom-type");
const chipVersionStr =
"0x0" + parseInt(session_data.get("wtv-system-chipversion")).toString(16);
const sysConfigHex =
"0x" + parseInt(session_data.get("wtv-system-sysconfig")).toString(16);
const capabilitiesTable = new WTVClientCapabilities().capabilities_table;
var wtv_system_version = session_data.get("wtv-system-version");
var wtv_client_bootrom_version = session_data.get("wtv-client-bootrom-version");
var wtv_client_serial_number = wtvshared.filterSSID(session_data.get("wtv-client-serial-number"));
var wtv_client_rom_type = session_data.get("wtv-client-rom-type");
var wtv_system_chipversion_str = session_data.get("wtv-system-chipversion");
var wtv_system_sysconfig_hex = parseInt(session_data.get("wtv-system-sysconfig")).toString(16);
// halen's sysconfig/chipversion stuff
const soloVersion = (chipVersionStr & 0xf00000) >> 0x14;
const soloFab = (chipVersionStr & 0xf0000) >> 0x10;
const boardType = (sysConfigHex & 0x7000) >> 0xc;
const boardRev = (sysConfigHex & 0xf00) >> 8;
const boardRevB = (sysConfigHex & 0xf0) >> 4;
var capabilities_table = new WTVClientCapabilities().capabilities_table;
// determine box ASIC type
switch (chipVersionStr >> 0x18) {
case 1:
chip = "FIDO1";
break;
case 3:
chip = `SOLO-${soloVersion}, fab ${soloFab}`;
break;
case 4:
chip = `SOLO2-${soloVersion}, fab ${soloFab}`; // don't know much about this one
break;
default:
chip = "?";
}
// ========================= LC2 SYSCONFIG DECODE START =========================
// determine box video type
if ((sysConfigHex & 8) == 0) video = "NTSC";
else video = "PAL";
data = `<html>
// determine box storage type
if ((sysConfigHex & 4) == 0) storage = "disk";
else storage = "flash";
// determine box CPU endianness
if ((sysConfigHex & 0x80000) == 0) endianness = "little";
else endianness = "big";
// determine box CPU type
if ((sysConfigHex & 0x100000) == 0) cpu = 5230;
else cpu = 4640;
// determine box CPU clock multiplier
if ((sysConfigHex & 0x20000) == 0) cpuMult = 3;
else cpuMult = 2;
// determine smartcard 0 support
if ((sysConfigHex & 0x400000) == 0) sc0 = "supported";
else sc0 = "not supported";
//determine smartcard 1 support
if ((sysConfigHex & 0x200000) == 0) sc1 = "supported";
else sc1 = "not supported";
// ========================= FCS SYSCONFIG DECODE START =========================
/* "I don't even know how it works."
-Bruce Leak, Thursday, October 12, 1995 1:53:28 AM */
// determine box CPU output bufs
if ((sysConfigHex & 0x2000) == 0) outputBufs = 100;
else outputBufs = 50;
// determine box SGRAM speed
function getSGSpeed() {
let SGRAMand = sysConfigHex & 0xc00000;
if (SGRAMand == 0x400000) return 66;
else if (0x400000 < SGRAMand)
if (SGRAMand == 0x800000) return 77;
else if (SGRAMand == 0xc00000) return 83; // potentially incorrect but looks like it should return 83MHz on known existing hardware
else if (SGRAMand == 0) return 100;
}
// determine box audio chip type
if ((sysConfigHex & 0xc0000) == 0xc0000) audio = "AKM4310/4309";
else audio = "Unknown";
// determine box audio clock source
if ((sysConfigHex & 0x20000) == 0) audioClk = "SPOT";
else audioClk = "External";
// determine box video chip
function getVideoChip() {
let videoChipAnd = sysConfigHex & 0x600;
if (videoChipAnd == 0x200) return "Bt851";
else if (videoChipAnd < 0x201 && videoChipAnd !== 0) return "Unknown";
else if (videoChipAnd == 0x400) return "Bt852";
else return "Philips7187/Bt866";
}
// determine box video type
if ((sysConfigHex & 0x800) == 0) videoB = "PAL";
else videoB = "NTSC";
// determine box video clock source
if ((sysConfigHex & 0x10000) == 0) videoClk = "External";
else videoClk = "SPOT";
// determine box board type
switch (sysConfigHex & 0xc) {
case 8:
boardTypeB = "Trial";
break;
case 0xc:
boardTypeB = "FCS";
break;
default:
boardTypeB = "Unknown Type";
}
// determine bank 0 type
if (sysConfigHex < 0) bank0Type = "Mask";
else bank0Type = "Flash";
// determine bank 0 mode
if ((sysConfigHex & 0x40000000) == 0) bank0Mode = "Normal";
else bank0Mode = "PageMode";
// determine bank 1 type
if ((sysConfigHex & 0x8000000) == 0) bank1Type = "Flash";
else bank1Type = "Mask";
// determine bank 1 mode
if ((sysConfigHex & 0x40000000) == 0) bank1Mode = "Normal";
else bank1Mode = "PageMode";
data = `<html>
<!--- *=* Copyright 1996, 1997 WebTV Networks, Inc. All rights reserved. --->
<display nosave nosend skipback>
<title>${minisrv_config.config.service_name} Info</title>
<sidebar width=20%>
<img src="wtv-tricks:/images/About_bg.jpg">
</sidebar>
<body bgcolor="#191919" text="#44cc55" link="36d5ff" vlink="36d5ff" vspace=0>
<br>
<br>
<br>
<h1>${minisrv_config.config.service_name} Info</h1>
<table cellspacing=0 cellpadding=0><tr><td abswidth=10>&nbsp;<td colspan=3>
<table>
<tr>
<td height=20>
<tr>
<td valign=top align=right><shadow>Connected to:</shadow>
<td width=10>
<td valign=top>Mini Service
<td valign=top>${minisrv_config.config.service_name} Service
<tr>
<td valign=top align=right><shadow>Host/Port:</shadow>
<td width=10>
<td valign=top>${service_ip}/${minisrv_config.services[service_name].port}
<td valign=top>${serviceIP}/${minisrv_config.services[service_name].port}
<tr>
<td valign=top align=right width=150><shadow>Service:</shadow>
<td width=10>
<td valign=top>${minisrv_version_string}
<td valign=top>${zTitle}
<tr>
<td valign=top align=right><shadow>Client:</shadow>
<td width=10>
<td valign=top>&vers; (Build ${wtv_system_version} [${client_label}])
<td valign=top>&vers; (Build ${systemVersion} [${getVersion(systemVersion)}])
<tr>
<td valign=top align=right><shadow>Boot:</shadow>
<td width=10>
<td valign=top>&wtv-bootvers; (Build ${wtv_client_bootrom_version} [${boot_client_label}])
<td valign=top>&wtv-bootvers; (Build ${bootromVersion} [${getVersion(bootromVersion)}])
<tr>
<td height=20)
<tr>
<td valign=top align=right><shadow>Silicon serial ID:</shadow>
<td width=10>
<td valign=top>${wtv_client_serial_number}
<td valign=top>${SSID}
<tr>
<td valign=top align=right><shadow>Connected at:</shadow>
<td width=10>
<td valign=top>&rate;
<tr>
<td valign=top align=right><shadow>POP Number:</shadow>
<td width=10>
<td valign=top>&phone;
<tr>
<td valign=top align=right><shadow>Client IP number:</shadow>
<td width=10>
<td valign=top>${socket.remoteAddress}
`;
if (session_data.getSessionData("registered")) {
data += `<tr>
if (session_data.getSessionData("registered")) {
data += `<tr>
<td valign=top align=right><shadow>Subscriber Name:</shadow>
<td width=10>
<td valign=top>${session_data.getSessionData("subscriber_name")}
<tr>
<td valign=top align=right><shadow>Subscriber Username:</shadow>
<td width=10>
<td valign=top>${session_data.getSessionData("subscriber_username")}
<tr>
<td valign=top align=right><shadow>Subscriber Contact:</shadow>
<td width=10>
<td valign=top>${session_data.getSessionData("subscriber_contact")} (${session_data.getSessionData("subscriber_contact_method")})`;
} else {
data += `<tr>
<td valign=top align=right><shadow>Unregistered Guest:</shadow>
<td width=10>
<td valign=top>Yes`;
}
<td valign=top>${session_data.getSessionData("subscriber_username")}`;
}
data += `<tr>
data += `<tr>
<td height=20>
<tr>
<td valign=top align=right><shadow>ROM type:</shadow>
<td width=10>
<td valign=top>${wtv_client_rom_type}
<td valign=top>${romType}
<tr>
<td valign=top align=right><shadow>Modem f/w (when available):</shadow>
<td width=10>
<td valign=top>&modem;
`;
if (session_data.get("wtv-need-upgrade")) {
data += `<tr>
<td valign=top align=right><shadow>Mini-browser:</shadow>
<td width=10>
<td valign=top>Yes
`;
}
data += `
<tr>
<td valign=top align=right><shadow>Chip version:</shadow>
<td width=10>
<td valign=top>${wtv_system_chipversion_str} (TODO)
<td valign=top>${chipVersionStr} (${chip})`;
if (sysConfigHex !== "0xNaN")
data += `
<tr>
<td valign=top align=right><shadow>SysConfig:</shadow>
<td width=10>
<td valign=top>0x${wtv_system_sysconfig_hex}
<td valign=top>${sysConfigHex}`;
data += `
</table>
<table>
@@ -139,33 +291,39 @@ Content-Type: text/html`
<table>
`;
// start loop
// start loop
Object.keys(capabilities_table).forEach(function (k) {
data += `<tr>
<td valign=top align=right>${capabilities_table[k][1]}
Object.keys(capabilitiesTable).forEach(function (k) {
data += `<tr>
<td valign=top align=right>${capabilitiesTable[k][1]}
<td width=10>
`;
if (client_caps[capabilities_table[k][0]]) data += "<td valign=top>True\n";
else data += "<td valign=top>False\n";
});
if (client_caps[capabilitiesTable[k][0]]) data += "<td valign=top>True\n";
else data += "<td valign=top>False\n";
});
// end loop
// end loop
data += `
data += `
</table>
<pre>
${wtv_system_sysconfig_str}
<pre>`
// TODO: finish FCS decode
if (romType == "bf0app" && sysConfigHex !== "0xNaN") {
data += `CPU Clk Mult = 2x Bus Clk, CPU output bufs @ ${outputBufs}%
ROM Bank 0: ${bank0Type}, ${bank0Mode}, 120ns/60ns
ROM Bank 1: ${bank1Type}, ${bank1Mode}, 150ns/75ns
SGRAM: ${getSGSpeed()}MHz
Audio: ${audio}, ${audioClk} Clk
Video: ${getVideoChip()}, ${videoB}, ${videoClk} Clk
Board: ${boardTypeB}, Rev = ${0xf - (boardRevB)} (${boardRevB})`;
} else if (sysConfigHex !== "0xNaN") {
data += `Video = ${video}, storage = ${storage}
CPU type = ${cpu}, ${endianness}-endian
CPU clock mult = ${cpuMult}x
SmartCard 0 ${sc0}, SmartCard 1 ${sc1}
Board type = ${boardType}, board rev = ${boardRev}`;
}
data += `
</pre>
<br>
</body> </html>`;
} else {
var errpage = wtvshared.doErrorPage(400);
headers = errpage[0];
data = errpage[1];
}
</body></html>`;