Merge branch 'dev'

This commit is contained in:
zefie
2021-08-08 22:27:47 -04:00
12 changed files with 147 additions and 88 deletions

View File

@@ -144,7 +144,7 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
headers += getServiceString('wtv-flashrom') + "\n";
if (bf0app_update) headers += "wtv-boot-url: " + gourl + "\n";
else {
headers += "wtv-boot-url: wtv-1800:/preregister?relogin=true";
headers += "wtv-boot-url: wtv-head-waiter:/relogin?relogin=true";
if (request_headers.query.guest_login) headers += "&guest_login=true";
headers += "\n";
}

View File

@@ -43,7 +43,7 @@ wtv-ticket: ${wtvsec_login.ticket_b64}
${getServiceString('wtv-register')}
${getServiceString('wtv-head-waiter')}
${getServiceString('wtv-star')}
wtv-boot-url: wtv-register:/splash?
wtv-boot-url: wtv-head-waiter:/relogin?
`
}
headers += `wtv-visit: ${gourl}
@@ -110,11 +110,11 @@ wtv-connection-timeout: 90
wtv-show-time-enabled: true
wtv-fader-timeout: 900
wtv-tourist-enabled: true`
headers += "\nwtv-relogin-url: wtv-1800:/preregister?relogin=true";
headers += "\nwtv-relogin-url: wtv-head-waiter:/relogin?relogin=true";
if (request_headers.query.guest_login) headers += "&guest_login=true";
headers += "\nwtv-reconnect-url: wtv-1800:/preregister?reconnect=true";
headers += "\nwtv-reconnect-url: wtv-head-waiter:/relogin?reconnect=true";
if (request_headers.query.guest_login) headers += "&guest_login=true";
headers += "\nwtv-boot-url: wtv-1800:/preregister?relogin=true";
headers += "\nwtv-boot-url: wtv-head-waiter:/relogin?relogin=true";
if (request_headers.query.guest_login) headers += "&guest_login=true";
headers += "\nwtv-allow-dsc: true";
headers += "\nwtv-home-url: wtv-home:/home?";

View File

@@ -53,8 +53,8 @@ wtv-expire-all: wtv-head-waiter:
wtv-log-url: wtv-log:/log`;
if (challenge_header != "") headers += "\n" + challenge_header;
headers += `
wtv-relogin-url: wtv-1800:/preregister?relogin=true
wtv-reconnect-url: wtv-1800:/preregister?reconnect=true
wtv-relogin-url: wtv-head-waiter:/relogin?relogin=true
wtv-reconnect-url: wwtv-head-waiter:/relogin?reconnect=true
wtv-visit: ${gourl}
Content-type: text/html`;
data = '';
@@ -66,7 +66,7 @@ Connection: Keep-Alive
Expires: Wed, 09 Oct 1991 22:00:00 GMT
wtv-expire-all: wtv-head-waiter:
wtv-expire-all: wtv-1800:
wtv-visit: wtv-1800:/preregister?relogin=true
wtv-visit: wtv-head-waiter:/relogin?relogin=true
Content-type: text/html`;
data = '';
}

View File

@@ -0,0 +1,20 @@
var gourl = "wtv-1800:/preregister?";
if (request_headers.query.relogin) gourl += "relogin=true";
else if (request_headers.query.reconnect) gourl += "reconnect=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";
}
headers = `200 OK
Connection: Keep-Alive
Expires: Wed, 09 Oct 1991 22:00:00 GMT
wtv-expire-all: wtv-head-waiter:
wtv-expire-all: wtv-1800:
wtv-service: reset
${getServiceString('wtv-1800')}
wtv-visit: ${gourl}
Content-type: text/html`;
data = '';

View File

@@ -249,17 +249,17 @@ WARRANTY OF ANY KIND. THE INFORMATION, SOFTWARE, PRODUCTS, AND SERVICES INCLUDED
THROUGH THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE MAY INCLUDE INACCURACIES OR TYPOGRAPHICAL ERRORS.
ADVICE RECEIVED VIA THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE SHOULD NOT BE RELIED UPON FOR PERSONAL,
MEDICAL, LEGAL OR FINANCIAL DECISIONS AND YOU SHOULD CONSULT AN APPROPRIATE
PROFESSIONAL FOR SPECIFIC ADVICE TAILORED TO YOUR SITUATION. MICROSOFT,
PROFESSIONAL FOR SPECIFIC ADVICE TAILORED TO YOUR SITUATION. ${WTVRegister.getServiceOperator().toUpperCase()},
ITS RESELLERS, DISTRIBUTORS AND/OR SUPPLIERS DO NOT WARRANT THAT ACCESS TO
OR USE OF THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE WILL BE UNINTERRUPTED OR ERROR-FREE, THAT MEMBERS
WILL BE ABLE TO ACCESS THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE AT ANY TIME OR IN ANY GEOGRAPHIC AREA,
OR THAT THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE OR ${WTVRegister.getServiceOperator().toUpperCase()} SOFTWARE OR SERVICES WILL MEET ANY
PARTICULAR CRITERIA OF PERFORMANCE OR QUALITY. MICROSOFT, ITS RESELLERS,
PARTICULAR CRITERIA OF PERFORMANCE OR QUALITY. ${WTVRegister.getServiceOperator().toUpperCase()}, ITS RESELLERS,
DISTRIBUTORS AND/OR SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES AND CONDITIONS
WITH REGARD TO THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE AND ALL RELATED SOFTWARE, INFORMATION,
PRODUCTS, SERVICES AND GRAPHICS, INCLUDING ALL IMPLIED WARRANTIES AND
CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, WORKMANLIKE
EFFORT, TITLE, AND NON-INFRINGEMENT. IN NO EVENT SHALL MICROSOFT, ITS
EFFORT, TITLE, AND NON-INFRINGEMENT. IN NO EVENT SHALL ${WTVRegister.getServiceOperator().toUpperCase()}, ITS
RESELLERS, DISTRIBUTORS AND/OR SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT,
PUNITIVE, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER, INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF USE, DATA, OR
@@ -281,7 +281,7 @@ DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR
INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY NOT APPLY TO YOU. IF YOU ARE
DISSATISFIED WITH ANY PORTION OF THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE, OR WITH ANY OF THESE
TERMS OF USE, YOUR SOLE AND EXCLUSIVE REMEDY IS TO NOT REGISTER FOR A ${minisrv_config.config.service_name.toUpperCase()}
SERVICE ACCOUNT OR TO TERMINATE YOUR ${minisrv_config.config.service_name.toUpperCase()} SERVICE ACCOUNT. MICROSOFT MAY,
SERVICE ACCOUNT OR TO TERMINATE YOUR ${minisrv_config.config.service_name.toUpperCase()} SERVICE ACCOUNT. ${WTVRegister.getServiceOperator().toUpperCase()} MAY,
IN ITS SOLE DISCRETION AND WITHOUT PRIOR NOTICE (I) RESTRICT OR LIMIT ACCESS
TO THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE; (II) TERMINATE A USER ACCOUNT OR USER SESSIONS AT ANY
TIME; OR (III) DISCONTINUE OR MODIFY ANY OR ALL ASPECTS OF THE ${minisrv_config.config.service_name.toUpperCase()} SERVICE OR ITS SERVICES.

View File

@@ -40,7 +40,7 @@ Welcome
ENCTYPE="x-www-form-encoded" METHOD="POST">
<input type=hidden name=registering value="true">
<td height=230 width= 300 bgcolor="#171726" colspan=5 valign=top align=left>
Welcome to the ${minisrv_config.config.service_name} Mini Service, operated by ${minisrv_config.config.service_owner}
Welcome to the ${minisrv_config.config.service_name} Mini Service, operated by ${minisrv_config.config.service_owner}.
The next screens will lead you through a quick setup process for using this service.<p> Press the "Continue" button below to begin setup.<p>
<td abswidth=20 bgcolor=#171726 >
</tr>

View File

@@ -202,7 +202,7 @@ class WTVClientSessionData {
try {
// only save if file has changed
var json_save_data = JSON.stringify(this.session_store);
var json_load_data = loadSessionData(true);
var json_load_data = this.loadSessionData(true);
if (json_save_data != json_load_data) this.fs.writeFileSync(this.session_storage + this.path.sep + this.ssid + ".json", JSON.stringify(this.session_store), "Utf8");
return true;
} catch (e) {

View File

@@ -15,12 +15,12 @@ class WTVLzpf {
current_length = 0;
current_literal = 0;
flag = 0xFFFF;
flag = 0xFFFFFFFF;
working_data = 0;
match_index = 0;
type_index = 0;
checksum = 0;
flag_table = new Uint16Array(0x1000)
flag_table = new Uint32Array(0x1000)
ring_buffer = new Uint8Array(0x2000)
encoded_data = [];
@@ -283,13 +283,13 @@ class WTVLzpf {
clear() {
this.current_length = 0;
this.current_literal = 0;
this.flag = 0xFFFF;
this.flag = 0xFFFFFFFF;
this.working_data = 0;
this.match_index = 0;
this.type_index = 0;
this.checksum = 0;
this.ring_buffer.fill(0x00, 0, 0x2000)
this.flag_table.fill(0xFFFF, 0, 0x1000);
this.flag_table.fill(0xFFFFFFFF, 0, 0x1000);
this.encoded_data = [];
}
@@ -364,28 +364,28 @@ class WTVLzpf {
this.type_index = 3;
} else {
this.match_index = (this.match_index + 1) & 0x1FFF;
this.flag = (this.flag + 1) & 0x1FFF;
this.flag = (this.flag + 1) & 0x1FFFFFFF;
this.checksum = (this.checksum + byte) & 0xFFFF;
this.working_data = ((this.working_data * 0x0100) + byte) & 0xFFFFFFFF;
i++;
}
} else {
this.flag = 0xFFFF;
this.flag = 0xFFFFFFFF;
if (i >= 3) {
flags_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF;
this.flag = this.flag_table[flags_index];
this.flag_table[flags_index] = i & 0x1FFF;
this.flag_table[flags_index] = i & 0x1FFFFFFF;
} else {
this.type_index++;
}
if (this.flag == 0xFFFF) {
if (this.flag == 0xFFFFFFFF) {
code_length = this.nomatchEncode[byte][1];
code = this.nomatchEncode[byte][0] << 0x10;
} else if (byte == this.ring_buffer[this.flag] && compress_data) {
this.match_index = 1;
this.flag = (this.flag + 1) & 0x1FFF;
this.flag = (this.flag + 1) & 0x1FFFFFFF;
this.type_index = 4;
} else {
code_length = this.nomatchEncode[byte][1] + 1;
@@ -427,7 +427,7 @@ class WTVLzpf {
var flags_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF;
var flag = this.flag_table[flags_index];
if (flag == 0xFFFF) {
if (flag == 0xFFFFFFFF) {
this.EncodeLiteral(0x10, 0x00990000);
} else {
this.EncodeLiteral(0x11, 0x004c8000);

View File

@@ -2,6 +2,7 @@
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const http = require('http');
const https = require('https');
const strftime = require('strftime'); // used externally by service scripts
@@ -100,68 +101,70 @@ function doErrorPage(code, data = null) {
function getConType(path) {
var file_ext = getFileExt(path).toLowerCase();
var wtv_mime_type = "";
var modern_mime_type = "";
// process WebTV overrides, fall back to generic mime lookup
switch (file_ext) {
case "aif":
return "audio/x-aif";
wtv_mime_type = "audio/x-aif";
case "aifc":
return "audio/x-aifc";
wtv_mime_type = "audio/x-aifc";
case "aiff":
return "audio/x-aiff";
wtv_mime_type = "audio/x-aiff";
case "ani":
return "x-wtv-animation";
wtv_mime_type = "x-wtv-animation";
case "brom":
return "binary/x-wtv-bootrom";
wtv_mime_type = "binary/x-wtv-bootrom";
case "cdf":
return "application/netcdf";
wtv_mime_type = "application/netcdf";
case "dat":
return "binary/cache-data";
wtv_mime_type = "binary/cache-data";
case "dl":
return "wtv/download-list";
wtv_mime_type = "wtv/download-list";
case "gsm":
return "audio/x-gsm";
wtv_mime_type = "audio/x-gsm";
case "gz":
return "application/gzip";
wtv_mime_type = "application/gzip";
case "ini":
return "wtv/jack-configuration";
wtv_mime_type = "wtv/jack-configuration";
case "mips-code":
return "code/x-wtv-code-mips";
wtv_mime_type = "code/x-wtv-code-mips";
case "o":
return "binary/x-wtv-approm";
wtv_mime_type = "binary/x-wtv-approm";
case "ram":
return "audio/x-pn-realaudio";
wtv_mime_type = "audio/x-pn-realaudio";
case "rom":
return "binary/x-wtv-flashblock";
wtv_mime_type = "binary/x-wtv-flashblock";
case "rsp":
return "wtv/jack-response";
wtv_mime_type = "wtv/jack-response";
case "swa":
case "swf":
return "application/x-shockwave-flash";
wtv_mime_type = "application/x-shockwave-flash";
case "srf":
case "spl":
return "wtv/jack-data";
wtv_mime_type = "wtv/jack-data";
case "ttf":
return "wtv/jack-fonts";
wtv_mime_type = "wtv/jack-fonts";
case "tvch":
return "wtv/tv-channels";
wtv_mime_type = "wtv/tv-channels";
case "tvl":
return "wtv/tv-listings";
wtv_mime_type = "wtv/tv-listings";
case "tvsl":
return "wtv/tv-smartlinks";
wtv_mime_type = "wtv/tv-smartlinks";
case "wad":
return "binary/doom-data";
wtv_mime_type = "binary/doom-data";
case "mp2":
case "hsb":
case "rmf":
case "s3m":
case "mod":
case "xm":
return "application/Music";
wtv_mime_type = "application/Music";
}
// if we reach here, its not a WebTV specific override
// or we are not yet aware of said override
return mime.lookup(path);
modern_mime_type = mime.lookup(path);
if (wtv_mime_type == "") wtv_mime_type = modern_mime_type;
return new Array(wtv_mime_type, modern_mime_type);
}
async function processPath(socket, service_vault_file_path, request_headers = new Array(), service_name) {
@@ -180,9 +183,10 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
service_vault_found = true;
request_is_async = true;
if (!zquiet) console.log(" * Found " + service_vault_file_path + " to handle request (Direct File Mode) [Socket " + socket.id + "]");
var contype = getConType(service_vault_file_path);
var contypes = getConType(service_vault_file_path);
headers = "200 OK\n"
headers += "Content-Type: " + contype;
headers += "Content-Type: " + contypes[0] + "\n";
headers += "wtv-modern-content-type" + contypes[1];
fs.readFile(service_vault_file_path, null, function (err, data) {
sendToClient(socket, headers, data);
});
@@ -585,11 +589,11 @@ async function sendToClient(socket, headers_obj, data) {
headers_obj = moveObjectElement('Connection', 'http_response', headers_obj);
}
var clen = 0;
var content_length = 0;
if (typeof data.length !== 'undefined') {
clen = data.length;
content_length = data.length;
} else if (typeof data.byteLength !== 'undefined') {
clen = data.byteLength;
content_length = data.byteLength;
}
// fix captialization
@@ -601,28 +605,80 @@ async function sendToClient(socket, headers_obj, data) {
// if box can do compression, see if its worth enabling
if (ssid_sessions[socket.ssid].capabilities) {
if (ssid_sessions[socket.ssid].capabilities['client-can-receive-compressed-data'] && minisrv_config.config.enable_lzpf_compression) {
compress_data = shouldWeCompress(headers_obj["Content-Type"]);
if (ssid_sessions[socket.ssid].capabilities['client-can-receive-compressed-data']) {
var compression_type = 1; // lzpf
if (ssid_sessions[socket.ssid].get("wtv-client-rom-type") != "bf0app") {
var compression_type = 2; // gzip
}
// mostly for debugging
if (minisrv_config.config.force_compression_type == "lzpf") compression_type = 1;
if (minisrv_config.config.force_compression_type == "gzip") compression_type = 2;
// should we bother to compress?
if (!headers_obj["Content-Encoding"]) {
if (typeof (headers_obj["Content-Type"]) != 'undefined') {
var content_type = (typeof (headers_obj["wtv-modern-content-type"]) != 'undefined') ? headers_obj["wtv-modern-content-type"] : headers_obj["Content-Type"];
// both lzpf and gzip
if (content_type.match(/^text\//) && headers_obj["Content-Type"] != "text/tellyscript") compress_data = true;
else if (content_type.match(/^application\/(x-?)javascript$/)) compress_data = true;
else if (content_type == "application/json") compress_data = true;
if (compression_type == 2) {
// gzip only
if (content_type.match(/^audio\/(x-)?[midi|wav]/)) compress_data = true; // midi & wav
if (content_type.match(/^audio\/(x-)?[s3m|mod|xm]/)) compress_data = true; // s3m, mod, xm
if (content_type.match(/^audio\/(x-)?[midi|wav]/)) compress_data = true; // midi & wav
}
}
}
if (headers_obj["wtv-modern-content-type"]) delete headers_obj["wtv-modern-content-type"];
}
}
// compress if needed
if (compress_data && clen > 0) {
content_length = clen;
if (compress_data && compression_type && content_length > 0) {
var uncompressed_content_length = content_length;
switch (compression_type) {
case 1:
// wtv-lzpf implementation
if (minisrv_config.config.enable_lzpf_compression || minisrv_config.config.force_compression_type) {
headers_obj["wtv-lzpf"] = 0;
var wtvcomp = new WTVLzpf();
data = wtvcomp.Compress(data);
wtvcomp = null; // Makes the garbage gods happy so it cleans up our mess
}
break;
case 2:
// zlib gzip implementation
if (minisrv_config.config.enable_gzip_compression || minisrv_config.config.force_compression_type) {
headers_obj['Content-Encoding'] = 'gzip';
data = zlib.gzipSync(data, {
'level': 9
});
}
break;
}
var compressed_content_length = 0;
if (content_length == 0 || compression_type != 1) {
// ultimately send compressed content length
compressed_content_length = data.byteLength;
content_length = compressed_content_length;
} else {
// ultimately send original content length if lzpf
compressed_content_length = data.byteLength;
}
var compression_percentage = ((compressed_content_length / uncompressed_content_length) * 100).toFixed(1).toString() + "%";
if (uncompressed_content_length != compressed_content_length) if (zdebug) console.log(" # Compression stats: Orig Size:", uncompressed_content_length, "~ Comp Size:", compressed_content_length, "~ Ratio:", compression_percentage);
}
// encrypt if needed
if (socket_sessions[socket.id].secure == true) {
headers_obj["wtv-encrypted"] = 'true';
headers_obj = moveObjectElement('wtv-encrypted', 'Connection', headers_obj);
if (clen > 0 && socket_sessions[socket.id].wtvsec) {
if (content_length > 0 && socket_sessions[socket.id].wtvsec) {
if (!zquiet) console.log(" * Encrypting response to client ...")
var enc_data = socket_sessions[socket.id].wtvsec.Encrypt(1, data);
data = enc_data;
@@ -634,14 +690,6 @@ async function sendToClient(socket, headers_obj, data) {
if (headers_obj["Content-Length"]) delete headers_obj["Content-Length"];
if (headers_obj["Content-length"]) delete headers_obj["Content-length"];
if (content_length == 0) {
if (typeof data.length !== 'undefined') {
content_length = data.length;
} else if (typeof data.byteLength !== 'undefined') {
content_length = data.byteLength;
}
}
headers_obj["Content-length"] = content_length;
if (ssid_sessions[socket.ssid]) {
@@ -713,19 +761,6 @@ async function sendToClient(socket, headers_obj, data) {
}
}
function shouldWeCompress(content_type) {
if (typeof (content_type) != 'undefined') {
if ((content_type.match(/^text\//) && content_type != "text/tellyscript") ||
content_type.match(/^application\/(x-?)javascript$/) ||
content_type.match(/^audio\/(x-)?midi/) ||
content_type.match(/^audio\/(x-)?wav/) ||
content_type == "application/json") {
return true;
}
}
return false;
}
function concatArrayBuffer(buffer1, buffer2) {
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);

View File

@@ -15,6 +15,7 @@
"verbosity": 2,
"error_log_file": "errors.log",
"enable_lzpf_compression": false,
"enable_gzip_compression": true,
"allow_guests": true
},
"services": {

View File

@@ -1,6 +1,6 @@
{
"name": "zefie_wtvp_minisrv",
"version": "0.9.13",
"version": "0.9.14",
"description": "WebTV Service (WTVP) Emulation Server",
"main": "app.js",
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv",

View File

@@ -71,6 +71,9 @@
<Content Include="ServiceVault\wtv-flashrom\ROMCache\up-arrows.swf" />
<Content Include="ServiceVault\wtv-flashrom\ROMCache\WebTVLogoJewel.gif" />
<Content Include="ServiceVault\wtv-flashrom\willie.js" />
<Content Include="ServiceVault\wtv-head-waiter\relogin.js">
<SubType>Code</SubType>
</Content>
<Content Include="ServiceVault\wtv-music\demo\hacktv4.gif" />
<Content Include="ServiceVault\wtv-music\demo\index.html" />
<Content Include="ServiceVault\wtv-music\demo\midi\acey.mid" />