- numerous bug fixes
 - add UTV tellyscripts
 - BREAKING CHANGE: doErrorPage moved to wtvshared.doErrorPage
	- but I fixed flashrom error handling..
	- added a passthrough for old scripts
 - added eMac's lzpf compression
 - Bump vm2 from 3.9.3 to 3.9.5 in /zefie_wtvp_minisrv
This commit is contained in:
zefie
2021-08-13 17:39:14 -04:00
parent 72c15305fa
commit 245e126298
57 changed files with 740 additions and 509 deletions

View File

@@ -1,7 +1,7 @@
# wtv minisrv node.js # wtv minisrv node.js
The ***wtv minisrv***, or "***zefie_wtvp_minisrv***" project is a node.js project that provides a mini WebTV Server, aiming for full WTVP (WebTV Protocol) support. The ***wtv minisrv***, or "***zefie_wtvp_minisrv***" project is a node.js project that provides a mini WebTV Server, aiming for full WTVP (WebTV Protocol) support.
This open source server is in alpha status. Use at your own risk. This open source server is in beta status. Use at your own risk.
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
@@ -14,22 +14,20 @@ This open source server is in alpha status. Use at your own risk.
- WebTV cookie support (wtv-cookie) for HTTP(s) - WebTV cookie support (wtv-cookie) for HTTP(s)
- Flashrom flashing support for all known units (including bf0app 'Old Classic') - Flashrom flashing support for all known units (including bf0app 'Old Classic')
- Can flash anything on [Ultra Willies](https://wtv.zefie.com/willie.php) with optional `use_zefie_server` flag set on `wtv-flashrom` service. - Can flash anything on [Ultra Willies](https://wtv.zefie.com/willie.php) with optional `use_zefie_server` flag set on `wtv-flashrom` service.
- `wtv-update:/sync` for Download-o-Rama style file downloading - `wtv-disk:/sync` for Download-o-Rama style file downloading
- Custom Tellyscripts *(not yet customizable though)* - Custom Tellyscripts *(not yet customizable though)*
- Flat file client session store and registration system - Flat file client session store and registration system
- wtv-lzpf compression support by eMac (99.9%)
### Current issues: ### Current issues:
- wtv-cookie implementation is still partial - Mis-configuring wtv-disk:/sync DiskMaps may cause units to delete contents of partitions (need more info)
- wtv-lzpf compression support is not yet reliable
- Mis-configuring wtv-update:/sync DiskMaps may cause units to delete contents of partitions (need more info)
- Satellite Receiver units reportedly cannot surf with http or https proxy. *(May be fixed by current partial wtv-cookie implementation)*
### Won't fix: ### Won't fix:
- wtv-encryption stream breaks when two different sessions have the same SSID (eg spoofing, won't fix (production did it too)) - wtv-encryption stream breaks when two different sessions have the same SSID (eg spoofing, won't fix (production did it too))
- ~~No intentions to support user accounts, registration, or any form of database system~~ *(I guess this was a lie, but we still don't use a database!)* - ~~No intentions to support user accounts, registration, or any form of database system~~ *(I guess this was a lie, but we still don't use a database!)*
### Feature Todo: ### Feature Todo:
- wtv-lzpf support *(Milestone v1.0)* - wtv-setup and bgm support
- TellyScript generation and/or manipulation without external dependancies - TellyScript generation and/or manipulation without external dependancies
- ~~wtv-cookie full support~~ ***Done [v0.9.13](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.13)*** - ~~wtv-cookie full support~~ ***Done [v0.9.13](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.13)***
- ~~Flashrom flashing for bf0app old classic~~ ***Done [v0.9.9](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.9)*** - ~~Flashrom flashing for bf0app old classic~~ ***Done [v0.9.9](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.9)***

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (request_headers.query.url) { if (request_headers.query.url) {
if (request_headers.query.url.indexOf(":/") > 0) { if (request_headers.query.url.indexOf(":/") > 0) {
var service_request = request_headers.query.url.split(":/")[0]; var service_request = request_headers.query.url.split(":/")[0];
@@ -31,7 +33,7 @@ if (request_headers.query.url) {
} }
if (!headers) { if (!headers) {
var errpage = doErrorPage(500) var errpage = wtvshared.doErrorPage(500)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Content-Type: text/html` Content-Type: text/html`

View File

@@ -1,7 +1,9 @@
var minisrv_service_file = true;
if (socket.ssid != null && !ssid_sessions[socket.ssid].get("wtvsec_login")) { if (socket.ssid != null && !ssid_sessions[socket.ssid].get("wtvsec_login")) {
var wtvsec_login = new WTVSec(minisrv_config); var wtvsec_login = new WTVSec(minisrv_config);
wtvsec_login.IssueChallenge(); wtvsec_login.IssueChallenge();
wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]); if (request_headers["wtv-incarnation"]) wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
ssid_sessions[socket.ssid].set("wtvsec_login", wtvsec_login); ssid_sessions[socket.ssid].set("wtvsec_login", wtvsec_login);
} else if (socket.ssid != null) { } else if (socket.ssid != null) {
var wtvsec_login = ssid_sessions[socket.ssid].get("wtvsec_login"); var wtvsec_login = ssid_sessions[socket.ssid].get("wtvsec_login");

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
var gourl = "wtv-1800:/finish-prereg?"; var gourl = "wtv-1800:/finish-prereg?";
if (request_headers.query.relogin) gourl += "relogin=true"; if (request_headers.query.relogin) gourl += "relogin=true";

View File

@@ -1,6 +1,8 @@
var gourl = "wtv-head-waiter:/login?"; var minisrv_service_file = true;
if (socket.ssid) { var gourl = "wtv-head-waiter:/login?";
if (socket.ssid) {
if (ssid_sessions[socket.ssid].loadSessionData() == true) { if (ssid_sessions[socket.ssid].loadSessionData() == true) {
console.log(" * Loaded session data from disk for", wtvshared.filterSSID(socket.ssid)) console.log(" * Loaded session data from disk for", wtvshared.filterSSID(socket.ssid))
ssid_sessions[socket.ssid].setSessionData("registered", (ssid_sessions[socket.ssid].getSessionData("registered") == true) ? true : false); ssid_sessions[socket.ssid].setSessionData("registered", (ssid_sessions[socket.ssid].getSessionData("registered") == true) ? true : false);
@@ -30,16 +32,16 @@
ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(minisrv_config); ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(minisrv_config);
ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge(); ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge();
ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"] || 1); if (request_headers["wtv-incarnation"]) ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
} else { } else {
console.log(" * Something bad happened (we don't know the client ssid???)"); console.log(" * Something bad happened (we don't know the client ssid???)");
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }
if (request_headers.query.relogin && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "relogin=true"; if (request_headers.query.relogin && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "relogin=true";
if (request_headers.query.reconnect && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "reconnect=true"; if (request_headers.query.reconnect && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "reconnect=true";
if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
var prereg_contype = "text/html"; var prereg_contype = "text/html";
@@ -66,7 +68,7 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
} else { } else {
romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type"); romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type");
} }
var file_path = null;
switch (romtype) { switch (romtype) {
case "US-LC2-disk-0MB-8MB": case "US-LC2-disk-0MB-8MB":
case "US-LC2-disk-0MB-8MB-softmodem-CPU5230": case "US-LC2-disk-0MB-8MB-softmodem-CPU5230":
@@ -75,15 +77,30 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
case "US-WEBSTAR-disk-0MB-16MB-softmodem-CPU5230": case "US-WEBSTAR-disk-0MB-16MB-softmodem-CPU5230":
prereg_contype = "text/tellyscript"; prereg_contype = "text/tellyscript";
// if wtv-open-access: true then client expects OpenISP // if wtv-open-access: true then client expects OpenISP
if (ssid_sessions[socket.ssid].get("wtv-open-access")) var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_OpenISP_56k.tok"; if (ssid_sessions[socket.ssid].get("wtv-open-access")) file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_OpenISP_56k.tok";
else var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_WTV_18006138199.tok"; else var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_WTV_18006138199.tok";
break; break;
case "US-DTV-disk-0MB-32MB-softmodem-CPU5230":
if (wtvshared.isMiniBrowser()) {
prereg_contype = "text/tellyscript";
if (ssid_sessions[socket.ssid].get("wtv-open-access")) file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_OpenISP_56k.tok";
else file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_WTV_18006138199.tok";
} else {
prereg_contype = "text/dialscript";
if (ssid_sessions[socket.ssid].get("wtv-lan") == "true") {
file_path = __dirname + "/ServiceDeps/premade_tellyscripts/UTV/utv_hsd.tok";
} else {
// todo OpenISP telly
file_path = __dirname + "/ServiceDeps/premade_tellyscripts/UTV/utv_normal.tok";
}
}
case "bf0app": case "bf0app":
prereg_contype = "text/tellyscript"; prereg_contype = "text/tellyscript";
// if wtv-open-access: true then client expects OpenISP // if wtv-open-access: true then client expects OpenISP
if (ssid_sessions[socket.ssid].get("wtv-open-access")) var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/bf0app/bf0app_OISP.tok"; if (ssid_sessions[socket.ssid].get("wtv-open-access")) file_path = __dirname + "/ServiceDeps/premade_tellyscripts/bf0app/bf0app_OISP.tok";
else var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/bf0app/bf0app_WTV_18006138199.tok"; else file_path = __dirname + "/ServiceDeps/premade_tellyscripts/bf0app/bf0app_WTV_18006138199.tok";
break; break;
// the following are not yet zefie generated and may have an unknown username/password attached // the following are not yet zefie generated and may have an unknown username/password attached
@@ -99,6 +116,11 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
data = ''; data = '';
break; break;
} }
if (socket.ssid.substr(0, 8) == "MSTVSIMU") {
prereg_contype = "text/dialscript";
var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/UTV/utv_hsd.tok";
}
} }
@@ -157,7 +179,7 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
request_is_async = true; request_is_async = true;
fs.readFile(file_path, null, function (err, file_read_data) { fs.readFile(file_path, null, function (err, file_read_data) {
if (err) { if (err) {
var errmsg = doErrorPage(400); var errmsg = wtvshared.doErrorPage(400);
headers = errmsg[0]; headers = errmsg[0];
file_read_data = errmsg[1] + "\n" + err.toString(); file_read_data = errmsg[1] + "\n" + err.toString();
} }
@@ -165,7 +187,7 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
}); });
} }
} else { } else {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = "200 OK"; headers = "200 OK";
if (request_headers.query.nick) headers += "\n" + ssid_sessions[socket.ssid].setIRCNick(request_headers.query.nick); if (request_headers.query.nick) headers += "\n" + ssid_sessions[socket.ssid].setIRCNick(request_headers.query.nick);
headers += "\nContent-Type: text/html"; headers += "\nContent-Type: text/html";
@@ -148,7 +150,7 @@ ${request_headers.query.channel}
</body> </body>
</html>`; </html>`;
} else { } else {
var errpage = doErrorPage("400 Chat requires host, port and channel arguments. Do not use the # on channels."); var errpage = wtvshared.doErrorPage("400 Chat requires host, port and channel arguments. Do not use the # on channels.");
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
var irc_nick = ""; var irc_nick = "";
headers = "200 OK"; headers = "200 OK";
if (request_headers.query.nick) headers += "\n" + ssid_sessions[socket.ssid].setIRCNick(request_headers.query.nick); if (request_headers.query.nick) headers += "\n" + ssid_sessions[socket.ssid].setIRCNick(request_headers.query.nick);

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (socket.ssid) { if (socket.ssid) {
if (request_headers.post_data) { if (request_headers.post_data) {
if (ssid_sessions[socket.ssid]) { if (ssid_sessions[socket.ssid]) {
@@ -9,7 +11,7 @@ if (socket.ssid) {
} }
if (!headers) { if (!headers) {
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (request_headers.post_data) { if (request_headers.post_data) {
if (request_headers.query.domain && request_headers.query.path) { if (request_headers.query.domain && request_headers.query.path) {
if (socket.ssid) { if (socket.ssid) {
@@ -11,7 +13,7 @@ if (request_headers.post_data) {
} }
if (!headers) { if (!headers) {
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (socket.ssid) { if (socket.ssid) {
if (ssid_sessions[socket.ssid]) { if (ssid_sessions[socket.ssid]) {
@@ -8,7 +10,7 @@ if (socket.ssid) {
} }
if (!headers) { if (!headers) {
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (socket.ssid) { if (socket.ssid) {
if (ssid_sessions[socket.ssid]) { if (ssid_sessions[socket.ssid]) {
ssid_sessions[socket.ssid].resetCookies(); ssid_sessions[socket.ssid].resetCookies();
@@ -19,7 +21,7 @@ Redirecting shortly... <a href="client:goback">Go Back</a>
} }
if (!headers) { if (!headers) {
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (request_headers.query.group) { if (request_headers.query.group) {
const WTVDownloadList = require("./WTVDownloadList.js"); const WTVDownloadList = require("./WTVDownloadList.js");
var wtvdl = new WTVDownloadList(minisrv_config, service_name); var wtvdl = new WTVDownloadList(minisrv_config, service_name);

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
const WTVDownloadList = require("./WTVDownloadList.js"); const WTVDownloadList = require("./WTVDownloadList.js");
var wtvdl = new WTVDownloadList(minisrv_config, service_name); var wtvdl = new WTVDownloadList(minisrv_config, service_name);
@@ -212,9 +214,7 @@ if (request_headers['wtv-request-type'] == 'download') {
Object.keys(service_vaults).forEach(function (g) { Object.keys(service_vaults).forEach(function (g) {
if (diskmap_data_file != null) return; if (diskmap_data_file != null) return;
diskmap_data_file = service_vaults[g] + "/" + service_name + "/" + diskmap_group_data.files[k].location; diskmap_data_file = service_vaults[g] + "/" + service_name + "/" + diskmap_group_data.files[k].location;
if (!fs.existsSync(diskmap_data_file)) { if (!fs.existsSync(diskmap_data_file)) diskmap_data_file = null;
console.error("Could not find a file for", diskmap_group_data.files[k].location, "(Last tried SV:", diskmap_data_file, ")");
}
}); });
var diskmap_file_stat = fs.lstatSync(diskmap_data_file); var diskmap_file_stat = fs.lstatSync(diskmap_data_file);
@@ -298,20 +298,20 @@ if (request_headers['wtv-request-type'] == 'download') {
headers = "200 OK\nContent-Type: wtv/download-list"; headers = "200 OK\nContent-Type: wtv/download-list";
} catch (e) { } catch (e) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
console.error(" # " + service_name+":/sync error", e); console.error(" # " + service_name+":/sync error", e);
} }
} }
} else { } else {
var errpage = doErrorPage(404, "The requested DiskMap does not exist."); var errpage = wtvshared.doErrorPage(404, "The requested DiskMap does not exist.");
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
if (minisrv_config.config.debug_flags.debug) console.error(" # " + service_name +":/sync error", "could not find diskmap"); if (minisrv_config.config.debug_flags.debug) console.error(" # " + service_name +":/sync error", "could not find diskmap");
} }
} else { } else {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
if (minisrv_config.config.debug_flags.debug) console.error(" # " + service_name + ":/sync error", "missing query arguments"); if (minisrv_config.config.debug_flags.debug) console.error(" # " + service_name + ":/sync error", "missing query arguments");

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (request_headers.post_data) { if (request_headers.post_data) {
if (request_headers.query.partialPath || request_headers.query.path) { if (request_headers.query.partialPath || request_headers.query.path) {
if (socket.ssid) { if (socket.ssid) {
@@ -15,7 +17,7 @@ if (request_headers.post_data) {
} }
if (!headers) { if (!headers) {
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
const WTVFlashrom = require("./WTVFlashrom.js"); const WTVFlashrom = require("./WTVFlashrom.js");
request_is_async = true; request_is_async = true;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
const WTVFlashrom = require("./WTVFlashrom.js"); const WTVFlashrom = require("./WTVFlashrom.js");
request_is_async = true; request_is_async = true;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
const WTVFlashrom = require("./WTVFlashrom.js"); const WTVFlashrom = require("./WTVFlashrom.js");
request_is_async = true; request_is_async = true;
@@ -28,7 +30,7 @@ if (request_headers.query.raw || bf0app_update) {
headers += "Content-type: text/html" headers += "Content-type: text/html"
data = ''; data = '';
} else { } else {
var errpage = doErrorPage(404) var errpage = wtvshared.doErrorPage(404)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,10 +1,11 @@
var minisrv_service_file = true;
const WTVFlashrom = require("./WTVFlashrom.js"); const WTVFlashrom = require("./WTVFlashrom.js");
var wtvflashrom; var wtvflashrom;
request_is_async = true; request_is_async = true;
if (!request_headers.query.path) { if (!request_headers.query.path) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} else { } else {
@@ -21,7 +22,7 @@ async function processLC2DownloadPage(path, flashrom_info, numparts = null) {
if (numparts != null) flashrom_info.part_count = parseInt(numparts); if (numparts != null) flashrom_info.part_count = parseInt(numparts);
if (!flashrom_info.part_count) flashrom_info.part_count = parseInt(flashrom_info.message.substring(flashrom_info.message.length - 4).replace(/\D/g, '')); if (!flashrom_info.part_count) flashrom_info.part_count = parseInt(flashrom_info.message.substring(flashrom_info.message.length - 4).replace(/\D/g, ''));
if (!flashrom_info.part_number || !flashrom_info.is_last_part || !flashrom_info.rompath || !flashrom_info.next_rompath || !flashrom_info.is_bootrom) { if (!flashrom_info.part_number || !flashrom_info.is_last_part || !flashrom_info.rompath || !flashrom_info.next_rompath || !flashrom_info.is_bootrom) {
if (!flashrom_info.is_last_part || request_headers.query.last_part) { if (!flashrom_info.is_last_part) {
flashrom_info.next_rompath = request_headers.request_url.replace(escape(request_headers.query.path), escape(flashrom_info.next_rompath.replace(service_name+":/",""))); flashrom_info.next_rompath = request_headers.request_url.replace(escape(request_headers.query.path), escape(flashrom_info.next_rompath.replace(service_name+":/","")));
} }
@@ -92,8 +93,7 @@ data += `
<upgradeblock width=280 height=15 <upgradeblock width=280 height=15
nexturl="${flashrom_info.next_rompath}" nexturl="${flashrom_info.next_rompath}"
errorurl="${service_name}:/lc2-download-failed?" errorurl="${service_name}:/lc2-download-failed?"
` blockurl="${flashrom_info.rompath}"`;
if (!flashrom_info.is_last_part) data += `blockurl = "${flashrom_info.rompath}"`;
data += ` data += `
lastblock="${flashrom_info.is_last_part}" lastblock="${flashrom_info.is_last_part}"
@@ -144,7 +144,7 @@ ${flashrom_info.message}
</body> </body>
</html>`; </html>`;
} else { } else {
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,9 +1,11 @@
var minisrv_service_file = true;
if (request_headers.query.path) { if (request_headers.query.path) {
var url = service_name + ":/get-lc2-page?path=" + request_headers.query.path; var url = service_name + ":/get-lc2-page?path=" + request_headers.query.path;
var romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type"); var romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type");
if (romtype == "bf0app") { if (romtype == "bf0app") {
url = "client:updateflash?ipaddr=" + minisrv_config.services[service_name].host + "&port=" + minisrv_config.services[service_name].port + "&path=" + escape(service_name + ":/" +request_headers.query.path); url = "client:updateflash?ipaddr=" + minisrv_config.services[service_name].host + "&port=" + minisrv_config.services[service_name].port + "&path=" + escape(service_name + ":/" +request_headers.query.path);
if (request_headers.query.numparts) url += escape("&numparts=" + request_headers.query.numparts); if (request_headers.query.numparts) url += escape("?numparts=" + request_headers.query.numparts);
} }
headers = "300 OK\n"; headers = "300 OK\n";
headers += "wtv-visit: " + url + "\n"; headers += "wtv-visit: " + url + "\n";
@@ -11,7 +13,7 @@ var romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type");
headers += "Content-type: text/html"; headers += "Content-type: text/html";
data = ''; data = '';
} else { } else {
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Connection: Close Connection: Close
wtv-connection-close: true wtv-connection-close: true
@@ -43,6 +45,7 @@ Updating complete
<img src="${service_name}:/ROMCache/S40H1.gif" width=560 height=6> <img src="${service_name}:/ROMCache/S40H1.gif" width=560 height=6>
<tr> <tr>
<td width=104 height=10 valign=top align=left> <td width=104 height=10 valign=top align=left>
<td width=20 valign=top align=left> <td width=20 valign=top align=left>
<td width=67 valign=top align=left> <td width=67 valign=top align=left>
<td width=20 valign=top align=left> <td width=20 valign=top align=left>

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
var error = ''; var error = '';
if (request_headers.query.error) { if (request_headers.query.error) {
switch (request_headers.query.error) { switch (request_headers.query.error) {

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
const WTVFlashrom = require("./WTVFlashrom.js"); const WTVFlashrom = require("./WTVFlashrom.js");
request_is_async = true; request_is_async = true;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
// willie is just a graphical frontend to a list of ROMs // willie is just a graphical frontend to a list of ROMs
// the rest of the scripts should work if you manually link to a ROM, and actually have it. // the rest of the scripts should work if you manually link to a ROM, and actually have it.
@@ -33,7 +35,7 @@ const req = https.request(options, function (res) {
res.on('error', function (e) { res.on('error', function (e) {
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Upstream Ultra Willies HTTP Error:", e); if (!minisrv_config.config.debug_flags.quiet) console.log(" * Upstream Ultra Willies HTTP Error:", e);
var errpage = doErrorPage(400) var errpage = wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
sendToClient(socket, headers, data); sendToClient(socket, headers, data);

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
var challenge_response, challenge_header = ''; var challenge_response, challenge_header = '';
var gourl; var gourl;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
var challenge_response, challenge_header = ''; var challenge_response, challenge_header = '';
var gourl; var gourl;
@@ -61,7 +63,7 @@ else {
if (request_headers.query.skip_splash) var home_url = "wtv-home:/home?"; if (request_headers.query.skip_splash) var home_url = "wtv-home:/home?";
else var home_url = "wtv-home:/splash?"; else var home_url = "wtv-home:/splash?";
} else if (!ssid_sessions[socket.ssid].getSessionData("registered")) { } else if (!ssid_sessions[socket.ssid].getSessionData("registered")) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} else { } else {

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
var challenge_response, challenge_header = ""; var challenge_response, challenge_header = "";
var gourl = "wtv-head-waiter:/login-stage-two?"; var gourl = "wtv-head-waiter:/login-stage-two?";

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
var gourl = "wtv-1800:/preregister?"; var gourl = "wtv-1800:/preregister?";
if (request_headers.query.relogin) gourl += "relogin=true"; if (request_headers.query.relogin) gourl += "relogin=true";
else if (request_headers.query.reconnect) gourl += "reconnect=true"; else if (request_headers.query.reconnect) gourl += "reconnect=true";

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers =`200 OK headers =`200 OK
Connection: Keep-Alive Connection: Keep-Alive
wtv-expire-all: wtv-home:/splash wtv-expire-all: wtv-home:/splash

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Connection: Keep-Alive Connection: Keep-Alive
wtv-expire-all: wtv- wtv-expire-all: wtv-

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
// write posted log data to disk. should be decrypted by this point (if it was encrypted) if the crypto stream didn't break // write posted log data to disk. should be decrypted by this point (if it was encrypted) if the crypto stream didn't break
request_is_async = true; request_is_async = true;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Content-Type: text/html`; Content-Type: text/html`;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (minisrv_config.config.allow_guests) { if (minisrv_config.config.allow_guests) {
headers = `300 Moved headers = `300 Moved
Connection: Close Connection: Close
@@ -20,7 +22,7 @@ wtv-reconnect-url: wtv-1800:/preregister?guest_login=true&reconnect=true
wtv-boot-url: wtv-1800:/preregister?guest_login=true wtv-boot-url: wtv-1800:/preregister?guest_login=true
Location: client:relogin`; Location: client:relogin`;
} else { } else {
var errpage = doErrorPage(400, "Guest mode is not enabled on this service."); var errpage = wtvshared.doErrorPage(400, "Guest mode is not enabled on this service.");
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `300 Moved headers = `300 Moved
Connection: Close Connection: Close
wtv-noback-all: wtv-register: wtv-noback-all: wtv-register:

View File

@@ -1,22 +1,22 @@
var minisrv_service_file = true;
if (!request_headers.query.registering) { if (!request_headers.query.registering) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} else { } else {
const WTVRegister = require("./WTVRegister.js") const WTVRegister = require("./WTVRegister.js")
var wtvr = new WTVRegister(minisrv_config, SessionStore); var wtvr = new WTVRegister(minisrv_config, SessionStore);
var errpage = null; var errpage = null;
if (!request_headers.query.registering) errpage = doErrorPage(400); if (!request_headers.query.registering) errpage = wtvshared.doErrorPage(400);
else if (!request_headers.query.subscriber_name) errpage = doErrorPage(400, "Please enter your name. This can be your real name, or your well-known online alias."); else if (!request_headers.query.subscriber_name) errpage = wtvshared.doErrorPage(400, "Please enter your name. This can be your real name, or your well-known online alias.");
else if (!request_headers.query.subscriber_username) errpage = doErrorPage(400, "Please enter a username."); else if (!request_headers.query.subscriber_username) errpage = wtvshared.doErrorPage(400, "Please enter a username.");
else if (request_headers.query.subscriber_username.length < 5) errpage = doErrorPage(400, "Please choose a username with 5 or more characters."); else if (request_headers.query.subscriber_username.length < 5) errpage = wtvshared.doErrorPage(400, "Please choose a username with 5 or more characters.");
else if (request_headers.query.subscriber_username.length > 16) errpage = doErrorPage(400, "Please choose a username with 16 or less characters."); else if (request_headers.query.subscriber_username.length > 16) errpage = wtvshared.doErrorPage(400, "Please choose a username with 16 or less characters.");
else if (!wtvr.checkUsernameSanity(request_headers.query.subscriber_username)) errpage = doErrorPage(400, "The username you have chosen contains invalid characters. Please choose a username with only <b>letters</b>, <b>numbers</b>, <b>_</b> or <b>-</b>. Also, please be sure your username begins with a letter."); else if (!wtvr.checkUsernameSanity(request_headers.query.subscriber_username)) errpage = wtvshared.doErrorPage(400, "The username you have chosen contains invalid characters. Please choose a username with only <b>letters</b>, <b>numbers</b>, <b>_</b> or <b>-</b>. Also, please be sure your username begins with a letter.");
else if (!wtvr.checkUsernameAvailable(request_headers.query.subscriber_username, ssid_sessions)) errpage = doErrorPage(400, "The username you have selected is already in use. Please select another username."); else if (!wtvr.checkUsernameAvailable(request_headers.query.subscriber_username, ssid_sessions)) errpage = wtvshared.doErrorPage(400, "The username you have selected is already in use. Please select another username.");
else if (!request_headers.query.subscriber_contact) errpage = doErrorPage(400, "Please enter your contact information."); else if (!request_headers.query.subscriber_contact) errpage = wtvshared.doErrorPage(400, "Please enter your contact information.");
else if (request_headers.query.subscriber_contact_method == "") errpage = doErrorPage(400, "Please select the type of contact information you provided."); else if (request_headers.query.subscriber_contact_method == "") errpage = wtvshared.doErrorPage(400, "Please select the type of contact information you provided.");
if (errpage) { if (errpage) {

View File

@@ -1,5 +1,7 @@
var minisrv_service_file = true;
if (!request_headers.query.registering) { if (!request_headers.query.registering) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} else { } else {

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
if (!request_headers.query.registering || if (!request_headers.query.registering ||
!request_headers.query.subscriber_name || !request_headers.query.subscriber_name ||
!request_headers.query.subscriber_username || !request_headers.query.subscriber_username ||
@@ -7,7 +9,7 @@ if (!request_headers.query.registering ||
!ssid_sessions[socket.ssid] || !ssid_sessions[socket.ssid] ||
!socket.ssid !socket.ssid
) { ) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} else { } else {
@@ -18,7 +20,7 @@ if (!request_headers.query.registering ||
ssid_sessions[socket.ssid].setSessionData("subscriber_userid", '1' + Math.floor(Math.random() * 1000000000000000000)); ssid_sessions[socket.ssid].setSessionData("subscriber_userid", '1' + Math.floor(Math.random() * 1000000000000000000));
ssid_sessions[socket.ssid].setSessionData("registered", true); ssid_sessions[socket.ssid].setSessionData("registered", true);
if (!ssid_sessions[socket.ssid].storeSessionData(true)) { if (!ssid_sessions[socket.ssid].storeSessionData(true)) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} else { } else {

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Content-Type: text/html`; Content-Type: text/html`;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Connection: Keep-Alive Connection: Keep-Alive
wtv-expire-all: wtv- wtv-expire-all: wtv-

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
wtv-backgroundmusic-load-playlist: wtv-music:/get-playlist wtv-backgroundmusic-load-playlist: wtv-music:/get-playlist
wtv-printer-model: -1,-1 wtv-printer-model: -1,-1

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
// Allow URL access outside our trusted minisrv // Allow URL access outside our trusted minisrv
if (request_headers.query.url) var url = request_headers.query.url; if (request_headers.query.url) var url = request_headers.query.url;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
wtv-expire-all: wtv- wtv-expire-all: wtv-
wtv-expire-all: http wtv-expire-all: http

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
wtv-noback-all: wtv- wtv-noback-all: wtv-
wtv-expire-all: wtv- wtv-expire-all: wtv-

View File

@@ -1,3 +1,4 @@
var minisrv_service_file = true;
var client_caps = null; var client_caps = null;
@@ -167,7 +168,7 @@ ${wtv_system_sysconfig_str}
</body> </html>`; </body> </html>`;
} else { } else {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Content-Type: text/html`; Content-Type: text/html`;

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Content-Type: text/html` Content-Type: text/html`

View File

@@ -1,3 +1,5 @@
var minisrv_service_file = true;
headers = `200 OK headers = `200 OK
Content-Type: text/html`; Content-Type: text/html`;

View File

@@ -8,51 +8,27 @@ class WTVFlashrom {
no_debug = false; no_debug = false;
service_name = ""; service_name = "";
minisrv_config = []; minisrv_config = [];
wtvshared = null;
constructor(minisrv_config, service_vaults, service_name, use_zefie_server = true, bf0app_update = false, no_debug = false) { constructor(minisrv_config, service_vaults, service_name, use_zefie_server = true, bf0app_update = false, no_debug = false) {
var { WTVShared } = require('./WTVShared.js');
this.service_vaults = service_vaults; this.service_vaults = service_vaults;
this.service_name = service_name; this.service_name = service_name;
this.use_zefie_server = use_zefie_server; this.use_zefie_server = use_zefie_server;
this.bf0app_update = bf0app_update; this.bf0app_update = bf0app_update;
this.no_debug = no_debug; this.no_debug = no_debug;
this.minisrv_config = minisrv_config; this.minisrv_config = minisrv_config;
this.wtvshared = new WTVShared(minisrv_config);
} }
doErrorPage(code, data = null) {
var headers = null;
switch (code) {
case 404:
if (data === null) data = "The service could not find the requested page.";
headers = "404 " + data + "\r\n";
headers += "Content-Type: text/html\r\n";
break;
case 400:
if (data === null) data = "HackTV ran into a technical problem.";
headers = "400 " + data + "\r\n";
headers += "Content-Type: text/html\r\n";
break;
default:
// what we send when we did not detect a wtv-url.
// e.g. when a pc browser connects
headers = "HTTP/1.1 200 OK\r\n";
headers += "Content-Type: text/html\r\n";
break;
}
console.error("doErrorPage Called:", code, data);
return new Array(headers, data);
}
async doLocalFlashROM(flashrom_file_path, request_path, callback, info_only = false) { async doLocalFlashROM(flashrom_file_path, request_path, callback, info_only = false) {
// use local flashrom files; // use local flashrom files;
console.log(info_only);
var self = this; var self = this;
try { try {
this.fs.readFile(flashrom_file_path, null, function (err, data) { this.fs.readFile(flashrom_file_path, null, function (err, data) {
if (err) { if (err) {
errpage = doErrorPage(400) errpage = wtvshared.doErrorPage(400)
var headers = errpage[0]; var headers = errpage[0];
data = err.toString(); data = err.toString();
callback(data, headers); callback(data, headers);
@@ -65,7 +41,7 @@ class WTVFlashrom {
} }
}); });
} catch (e) { } catch (e) {
var errpage = doErrorPage(404, "The service could not find the requested ROM.") var errpage = wtvshared.doErrorPage(404, "The service could not find the requested ROM.")
var headers = errpage[0]; var headers = errpage[0];
var data = errpage[1]; var data = errpage[1];
callback(data, headers); callback(data, headers);
@@ -107,24 +83,25 @@ class WTVFlashrom {
flashrom_info.total_parts_size = data.readUInt32BE(32); flashrom_info.total_parts_size = data.readUInt32BE(32);
flashrom_info.percent_complete = ((((flashrom_info.byte_progress + flashrom_info.part_total_size) / flashrom_info.total_parts_size)) * 100).toFixed(1); flashrom_info.percent_complete = ((((flashrom_info.byte_progress + flashrom_info.part_total_size) / flashrom_info.total_parts_size)) * 100).toFixed(1);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Part Size :", flashrom_info.part_total_size); if (this.minisrv_config.config.debug_flags.debug && !this.minisrv_config.config.debug_flags.quiet && !this.no_debug) console.log(" # Flashrom Part Size :", flashrom_info.part_total_size);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Bytes Sent :", flashrom_info.byte_progress); if (this.minisrv_config.config.debug_flags.debug && !this.minisrv_config.config.debug_flags.quiet && !this.no_debug) console.log(" # Flashrom Bytes Sent :", flashrom_info.byte_progress);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Bytes Sent+:", flashrom_info.byte_progress + flashrom_info.part_total_size, "(" + flashrom_info.percent_complete + "% complete)"); if (this.minisrv_config.config.debug_flags.debug && !this.minisrv_config.config.debug_flags.quiet && !this.no_debug) console.log(" # Flashrom Bytes Sent+:", flashrom_info.byte_progress + flashrom_info.part_total_size, "(" + flashrom_info.percent_complete + "% complete)");
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Total Size :", flashrom_info.total_parts_size); if (this.minisrv_config.config.debug_flags.debug && !this.minisrv_config.config.debug_flags.quiet && !this.no_debug) console.log(" # Flashrom Total Size :", flashrom_info.total_parts_size);
// read current part number bit from part header // read current part number bit from part header
flashrom_info.part_number = data.readUInt16BE(28); flashrom_info.part_number = data.readUInt16BE(28);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Curr Part Number :", flashrom_info.part_number); if (this.minisrv_config.config.debug_flags.debug && !this.minisrv_config.config.debug_flags.quiet && !this.no_debug) console.log(" # Flashrom Curr Part Number :", flashrom_info.part_number);
flashrom_info.is_last_part = ((flashrom_info.byte_progress + flashrom_info.part_total_size) == flashrom_info.total_parts_size) ? true : false; flashrom_info.is_last_part = ((flashrom_info.byte_progress + flashrom_info.part_total_size) == flashrom_info.total_parts_size) ? true : false;
if (flashrom_info.is_last_part) { if (flashrom_info.is_last_part) {
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Curr Part is Last:", flashrom_info.is_last_part); if (this.minisrv_config.config.debug_flags.debug && !this.minisrv_config.config.debug_flags.quiet && !this.no_debug) console.log(" # Flashrom Curr Part is Last:", flashrom_info.is_last_part);
} else { } else {
flashrom_info.next_part_number = flashrom_info.part_number + 1; flashrom_info.next_part_number = flashrom_info.part_number + 1;
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Next Part Number :", flashrom_info.next_part_number); if (this.minisrv_config.config.debug_flags.debug && !this.minisrv_config.config.debug_flags.quiet && !this.no_debug) console.log(" # Flashrom Next Part Number :", flashrom_info.next_part_number);
} }
if (this.minisrv_config.config.debug_flags.debug && this.minisrv_config.config.debug_flags.quiet) console.log(" # Sending", (flashrom_info.is_last_part) ? "Last Flashrom" : "Flashrom", "Part", flashrom_info.part_number, "- Bytes Sent:", flashrom_info.byte_progress + flashrom_info.part_total_size, "of", flashrom_info.total_parts_size, "(" + flashrom_info.percent_complete + " % complete)");
// read current part display message from part header // read current part display message from part header
flashrom_info.message = new Buffer.from(part_header.toString('hex').substring(36 * 2, 68 * 2), 'hex').toString('ascii').replace(/[^0-9a-z\ \.\-]/gi, ""); flashrom_info.message = new Buffer.from(part_header.toString('hex').substring(36 * 2, 68 * 2), 'hex').toString('ascii').replace(/[^0-9a-z\ \.\-]/gi, "");
flashrom_info.rompath = `wtv-flashrom:/${path}`; flashrom_info.rompath = `wtv-flashrom:/${path}`;
@@ -142,7 +119,6 @@ class WTVFlashrom {
async sendToClient(data, request_path, callback) { async sendToClient(data, request_path, callback) {
var headers = "200 OK\n"; var headers = "200 OK\n";
if (this.bf0app_update) headers += "minisrv-use-carriage-return: false\n";
var flashrom_info = this.getFlashromInfo(data, request_path) var flashrom_info = this.getFlashromInfo(data, request_path)
if (flashrom_info.is_bootrom) headers += "Content-Type: binary/x-wtv-bootrom"; // maybe? if (flashrom_info.is_bootrom) headers += "Content-Type: binary/x-wtv-bootrom"; // maybe?
else headers += "Content-Type: binary/x-wtv-flashblock"; else headers += "Content-Type: binary/x-wtv-flashblock";
@@ -193,15 +169,16 @@ class WTVFlashrom {
} else if (res.statusCode == 206) { } else if (res.statusCode == 206) {
var data = self.getFlashromInfo(Buffer.from(data_hex, 'hex'), request_path); var data = self.getFlashromInfo(Buffer.from(data_hex, 'hex'), request_path);
} else if (res.statusCode == 404) { } else if (res.statusCode == 404) {
var errpage = doErrorPage(404, "The service could not find the requested ROM on zefie's server.") console.log(request_path);
var errpage = self.wtvshared.doErrorPage(404, "The service could not find the requested ROM on zefie's server.")
headers = errpage[0]; headers = errpage[0];
var data = errpage[1]; var data = errpage[1];
} else { } else {
var errpage = doErrorPage(400) var errpage = self.wtvshared.doErrorPage(400)
headers = errpage[0]; headers = errpage[0];
var data = errpage[1]; var data = errpage[1];
} }
if (res.statusCode != 206) { if (!headers && res.statusCode != 206) {
self.sendToClient(data, request_path, callback); self.sendToClient(data, request_path, callback);
} else { } else {
callback(data, headers); callback(data, headers);

View File

@@ -1,118 +1,154 @@
/** /**
* Pure-JS implementation of WebTV's LZPF compression * Pure-JS implementation of WebTV's LZPF compression
* *
* This is a port of my Lzpf compression code from my ROMFS Python tool * This compression algorithm is based on LZP by Charles Bloom and was originally written for server to client communication by Andy McFadden
* Originally reverse engineered from the box * This uses a (static) Huffman dictionary that was tuned for character occurances in a typical HTML page at the time (around 1996-1997).
* *
* By: Eric MacDonald (eMac) * Andy McFadden:
* Modified By: zefie * https://fadden.com/
*/ * LZP:
* https://cbloom.com/src/index_lz.html
* https://en.wikibooks.org/wiki/Data_Compression/Dictionary_compression#LZP
*
* I wouldn't recommend using LZPF on anything but HTML and other text-based data (unless the data has many repeating bytes)
* LZPF can be replaced with gzip for LC2 and newer boxes. Classic is stuck with LZPF.
*
* Reverse engineered and ported by: Eric MacDonald (eMac)
* Modified By: zefie
**/
class WTVLzpf { class WTVLzpf {
// Note: currentlty doesn't offer optimal streaming support but this is good enough to meet perf demands at the scale we're at. // Note: currentlty doesn't offer optimal streaming support but this is good enough to meet perf demands at the scale we're at.
current_length = 0; current_bit_length = 0;
current_literal = 0; current_bits = 0;
flag = 0xFFFF; ring_bufer_index = 0xFFFF;
working_data = 0; working_data = 0;
match_index = 0; match_index = 0;
type_index = 0; compression_mode = 0;
checksum = 0; checksum = 0;
flag_table = new Uint16Array(0x1000) filler_byte = 0x20
hash_table = new Uint16Array(0x1000)
ring_buffer = new Uint8Array(0x2000) ring_buffer = new Uint8Array(0x2000)
encoded_data = []; encoded_data = [];
/**
* This is used to encode (one-byte) literals with no previous tracked occurence.
*
* - Bytes with best compression: SPACE and LF and e"/<>Tainoprst
* - Bytes with good compression: TAB and ,-.1=ABCDEFGHILNOPRSbcdfghlmuw
* - Bytes that don't change the length of the bit stream: 024:MW_kvy
* - The rest will increase the length of bit stream
*
* I don't know what process they used to build this table. I assume they
* frequency-scanned a bunch of HTML files they had.
*
* Using Windows-1252 (based off of ISO-8859-1) chracter encoding to fill in this table. Didn't
* seem like they used a different table for Japan builds (ISO-2022-JP).
**/
nomatchEncode = [ nomatchEncode = [
/* [FLATTENED HUFFMAN CODE, CODE BIT LENGTH] */
[0x0000, 0x10], [0x0001, 0x10], [0x0002, 0x10], [0x0000, 0x10] /* NUL */, [0x0001, 0x10] /* SOH */, [0x0002, 0x10] /* STX */,
[0x0003, 0x10], [0x0004, 0x10], [0x009A, 0x0F], [0x0003, 0x10] /* ETX */, [0x0004, 0x10] /* EOT */, [0x009A, 0x0F] /* ENQ */,
[0x0005, 0x10], [0x009C, 0x0F], [0x009E, 0x0F], [0x0005, 0x10] /* ACK */, [0x009C, 0x0F] /* BEL */, [0x009E, 0x0F] /* BS */,
[0x3400, 0x06], [0x7000, 0x05], [0x00A0, 0x0F], [0x3400, 0x06] /* TAB */, [0x7000, 0x05] /* LF */, [0x00A0, 0x0F] /* VT */,
[0x0006, 0x10], [0x0380, 0x09], [0x0007, 0x10], [0x0006, 0x10] /* FF */, [0x0380, 0x09] /* CR */, [0x0007, 0x10] /* SO */,
[0x0008, 0x10], [0x0009, 0x10], [0x000A, 0x10], [0x0008, 0x10] /* SI */, [0x0009, 0x10] /* DLE */, [0x000A, 0x10] /* DC1 */,
[0x000B, 0x10], [0x000C, 0x10], [0x000D, 0x10], [0x000B, 0x10] /* DC2 */, [0x000C, 0x10] /* DC3 */, [0x000D, 0x10] /* DC4 */,
[0x000E, 0x10], [0x000F, 0x10], [0x00A2, 0x0F], [0x000E, 0x10] /* NAK */, [0x000F, 0x10] /* SYN */, [0x00A2, 0x0F] /* BTB */,
[0x0010, 0x10], [0x0011, 0x10], [0x0012, 0x10], [0x0010, 0x10] /* CAN */, [0x0011, 0x10] /* EM */, [0x0012, 0x10] /* SUB */,
[0x0013, 0x10], [0x0014, 0x10], [0x0015, 0x10], [0x0013, 0x10] /* ESC */, [0x0014, 0x10] /* FS */, [0x0015, 0x10] /* GS */,
[0x0016, 0x10], [0x0017, 0x10], [0xE000, 0x04], [0x0016, 0x10] /* RS */, [0x0017, 0x10] /* US */, [0xE000, 0x04] /* SPACE */,
[0x0200, 0x0A], [0x7800, 0x05], [0x0400, 0x09], [0x0200, 0x0A] /* ! */, [0x7800, 0x05] /* " */, [0x0400, 0x09] /* # */,
[0x00B0, 0x0E], [0x0018, 0x10], [0x0120, 0x0B], [0x00B0, 0x0E] /* $ */, [0x0018, 0x10] /* % */, [0x0120, 0x0B] /* & */,
[0x0480, 0x09], [0x0140, 0x0B], [0x0160, 0x0B], [0x0480, 0x09] /* ' */, [0x0140, 0x0B] /* ( */, [0x0160, 0x0B] /* ) */,
[0x0240, 0x0A], [0x00B8, 0x0D], [0x1400, 0x07], [0x0240, 0x0A] /* * */, [0x00B8, 0x0D] /* + */, [0x1400, 0x07] /* , */,
[0x1600, 0x07], [0x3800, 0x06], [0x8000, 0x05], [0x1600, 0x07] /* - */, [0x3800, 0x06] /* . */, [0x8000, 0x05] /* / */,
[0x0A00, 0x08], [0x1800, 0x07], [0x0B00, 0x08], [0x0A00, 0x08] /* 0 */, [0x1800, 0x07] /* 1 */, [0x0B00, 0x08] /* 2 */,
[0x0500, 0x09], [0x0C00, 0x08], [0x0580, 0x09], [0x0500, 0x09] /* 3 */, [0x0C00, 0x08] /* 4 */, [0x0580, 0x09] /* 5 */,
[0x0600, 0x09], [0x0680, 0x09], [0x0700, 0x09], [0x0600, 0x09] /* 6 */, [0x0680, 0x09] /* 7 */, [0x0700, 0x09] /* 8 */,
[0x0780, 0x09], [0x0D00, 0x08], [0x0180, 0x0B], [0x0780, 0x09] /* 9 */, [0x0D00, 0x08] /* : */, [0x0180, 0x0B] /* ; */,
[0x8800, 0x05], [0x3C00, 0x06], [0x9000, 0x05], [0x8800, 0x05] /* < */, [0x3C00, 0x06] /* = */, [0x9000, 0x05] /* > */,
[0x0280, 0x0A], [0x00B4, 0x0E], [0x4000, 0x06], [0x0280, 0x0A] /* ? */, [0x00B4, 0x0E] /* @ */, [0x4000, 0x06] /* A */,
[0x1A00, 0x07], [0x1C00, 0x07], [0x1E00, 0x07], [0x1A00, 0x07] /* B */, [0x1C00, 0x07] /* C */, [0x1E00, 0x07] /* D */,
[0x4400, 0x06], [0x2000, 0x07], [0x2200, 0x07], [0x4400, 0x06] /* E */, [0x2000, 0x07] /* F */, [0x2200, 0x07] /* G */,
[0x2400, 0x07], [0x4800, 0x06], [0x01A0, 0x0B], [0x2400, 0x07] /* H */, [0x4800, 0x06] /* I */, [0x01A0, 0x0B] /* J */,
[0x02C0, 0x0A], [0x2600, 0x07], [0x0E00, 0x08], [0x02C0, 0x0A] /* K */, [0x2600, 0x07] /* L */, [0x0E00, 0x08] /* M */,
[0x4C00, 0x06], [0x5000, 0x06], [0x2800, 0x07], [0x4C00, 0x06] /* N */, [0x5000, 0x06] /* O */, [0x2800, 0x07] /* P */,
[0x00C0, 0x0C], [0x5400, 0x06], [0x2A00, 0x07], [0x00C0, 0x0C] /* Q */, [0x5400, 0x06] /* R */, [0x2A00, 0x07] /* S */,
[0x9800, 0x05], [0x0800, 0x09], [0x0880, 0x09], [0x9800, 0x05] /* T */, [0x0800, 0x09] /* U */, [0x0880, 0x09] /* V */,
[0x0F00, 0x08], [0x00D0, 0x0C], [0x0300, 0x0A], [0x0F00, 0x08] /* W */, [0x00D0, 0x0C] /* X */, [0x0300, 0x0A] /* Y */,
[0x0900, 0x09], [0x0019, 0x10], [0x001A, 0x10], [0x0900, 0x09] /* Z */, [0x0019, 0x10] /* [ */, [0x001A, 0x10] /* \ */,
[0x001B, 0x10], [0x001C, 0x10], [0x1000, 0x08], [0x001B, 0x10] /* ] */, [0x001C, 0x10] /* ^ */, [0x1000, 0x08] /* _ */,
[0x001D, 0x10], [0xA000, 0x05], [0x2C00, 0x07], [0x001D, 0x10] /* ` */, [0xA000, 0x05] /* a */, [0x2C00, 0x07] /* b */,
[0x5800, 0x06], [0x5C00, 0x06], [0xF000, 0x04], [0x5800, 0x06] /* c */, [0x5C00, 0x06] /* d */, [0xF000, 0x04] /* e */,
[0x2E00, 0x07], [0x3000, 0x07], [0x6000, 0x06], [0x2E00, 0x07] /* f */, [0x3000, 0x07] /* g */, [0x6000, 0x06] /* h */,
[0xA800, 0x05], [0x01C0, 0x0B], [0x1100, 0x08], [0xA800, 0x05] /* i */, [0x01C0, 0x0B] /* j */, [0x1100, 0x08] /* k */,
[0x6400, 0x06], [0x6800, 0x06], [0xB000, 0x05], [0x6400, 0x06] /* l */, [0x6800, 0x06] /* m */, [0xB000, 0x05] /* n */,
[0xB800, 0x05], [0xC000, 0x05], [0x01E0, 0x0B], [0xB800, 0x05] /* o */, [0xC000, 0x05] /* p */, [0x01E0, 0x0B] /* q */,
[0xC800, 0x05], [0xD000, 0x05], [0xD800, 0x05], [0xC800, 0x05] /* r */, [0xD000, 0x05] /* s */, [0xD800, 0x05] /* t */,
[0x3200, 0x07], [0x1200, 0x08], [0x6C00, 0x06], [0x3200, 0x07] /* u */, [0x1200, 0x08] /* v */, [0x6C00, 0x06] /* w */,
[0x0980, 0x09], [0x1300, 0x08], [0x0340, 0x0A], [0x0980, 0x09] /* x */, [0x1300, 0x08] /* y */, [0x0340, 0x0A] /* z */,
[0x00E0, 0x0C], [0x00F0, 0x0C], [0x0100, 0x0C], [0x00E0, 0x0C] /* { */, [0x00F0, 0x0C] /* | */, [0x0100, 0x0C] /* } */,
[0x0110, 0x0C], [0x001E, 0x10], [0x001F, 0x10], [0x0110, 0x0C] /* ~ */, [0x001E, 0x10] /* DEL */, [0x001F, 0x10] /* <20> */,
[0x0020, 0x10], [0x0021, 0x10], [0x0022, 0x10], [0x0020, 0x10] /* */, [0x0021, 0x10] /* <20> */, [0x0022, 0x10] /* <20> */,
[0x0023, 0x10], [0x0024, 0x10], [0x0025, 0x10], [0x0023, 0x10] /* <20> */, [0x0024, 0x10] /* <20> */, [0x0025, 0x10] /* <20> */,
[0x0026, 0x10], [0x0027, 0x10], [0x0028, 0x10], [0x0026, 0x10] /* <20> */, [0x0027, 0x10] /* <20> */, [0x0028, 0x10] /* <20> */,
[0x0029, 0x10], [0x002A, 0x10], [0x002B, 0x10], [0x0029, 0x10] /* <20> */, [0x002A, 0x10] /* <20> */, [0x002B, 0x10] /* <20> */,
[0x002C, 0x10], [0x002D, 0x10], [0x002E, 0x10], [0x002C, 0x10] /* */, [0x002D, 0x10] /* <20> */, [0x002E, 0x10] /* */,
[0x002F, 0x10], [0x00A4, 0x0F], [0x00A6, 0x0F], [0x002F, 0x10] /* */, [0x00A4, 0x0F] /* <20> */, [0x00A6, 0x0F] /* <20> */,
[0x00A8, 0x0F], [0x0030, 0x10], [0x0031, 0x10], [0x00A8, 0x0F] /* <20> */, [0x0030, 0x10] /* <20> */, [0x0031, 0x10] /* <20> */,
[0x0032, 0x10], [0x0033, 0x10], [0x0034, 0x10], [0x0032, 0x10] /* <20> */, [0x0033, 0x10] /* <20> */, [0x0034, 0x10] /* <20> */,
[0x0035, 0x10], [0x0036, 0x10], [0x0037, 0x10], [0x0035, 0x10] /* <20> */, [0x0036, 0x10] /* <20> */, [0x0037, 0x10] /* <20> */,
[0x0038, 0x10], [0x0039, 0x10], [0x003A, 0x10], [0x0038, 0x10] /* <20> */, [0x0039, 0x10] /* */, [0x003A, 0x10] /* <20> */,
[0x003B, 0x10], [0x003C, 0x10], [0x003D, 0x10], [0x003B, 0x10] /* <20> */, [0x003C, 0x10] /* NBSP*/, [0x003D, 0x10] /* <20> */,
[0x003E, 0x10], [0x003F, 0x10], [0x0040, 0x10], [0x003E, 0x10] /* <20> */, [0x003F, 0x10] /* <20> */, [0x0040, 0x10] /* <20> */,
[0x0041, 0x10], [0x0042, 0x10], [0x0043, 0x10], [0x0041, 0x10] /* <20> */, [0x0042, 0x10] /* <20> */, [0x0043, 0x10] /* <20> */,
[0x0044, 0x10], [0x0045, 0x10], [0x0046, 0x10], [0x0044, 0x10] /* <20> */, [0x0045, 0x10] /* <20> */, [0x0046, 0x10] /* <20> */,
[0x0047, 0x10], [0x0048, 0x10], [0x0049, 0x10], [0x0047, 0x10] /* <20> */, [0x0048, 0x10] /* <20> */, [0x0049, 0x10] /* SHY */,
[0x004A, 0x10], [0x004B, 0x10], [0x004C, 0x10], [0x004A, 0x10] /* <20> */, [0x004B, 0x10] /* <20> */, [0x004C, 0x10] /* <20> */,
[0x004D, 0x10], [0x004E, 0x10], [0x004F, 0x10], [0x004D, 0x10] /* <20> */, [0x004E, 0x10] /* <20> */, [0x004F, 0x10] /* <20> */,
[0x0050, 0x10], [0x0051, 0x10], [0x0052, 0x10], [0x0050, 0x10] /* <20> */, [0x0051, 0x10] /* <20> */, [0x0052, 0x10] /* <20> */,
[0x0053, 0x10], [0x0054, 0x10], [0x0055, 0x10], [0x0053, 0x10] /* <20> */, [0x0054, 0x10] /* <20> */, [0x0055, 0x10] /* <20> */,
[0x0056, 0x10], [0x0057, 0x10], [0x0058, 0x10], [0x0056, 0x10] /* <20> */, [0x0057, 0x10] /* <20> */, [0x0058, 0x10] /* <20> */,
[0x0059, 0x10], [0x005A, 0x10], [0x005B, 0x10], [0x0059, 0x10] /* <20> */, [0x005A, 0x10] /* <20> */, [0x005B, 0x10] /* <20> */,
[0x005C, 0x10], [0x005D, 0x10], [0x005E, 0x10], [0x005C, 0x10] /* <20> */, [0x005D, 0x10] /* <20> */, [0x005E, 0x10] /* <20> */,
[0x005F, 0x10], [0x0060, 0x10], [0x0061, 0x10], [0x005F, 0x10] /* <20> */, [0x0060, 0x10] /* <20> */, [0x0061, 0x10] /* <20> */,
[0x0062, 0x10], [0x00AA, 0x0F], [0x0063, 0x10], [0x0062, 0x10] /* <20> */, [0x00AA, 0x0F] /* <20> */, [0x0063, 0x10] /* <20> */,
[0x0064, 0x10], [0x0065, 0x10], [0x0066, 0x10], [0x0064, 0x10] /* <20> */, [0x0065, 0x10] /* <20> */, [0x0066, 0x10] /* <20> */,
[0x0067, 0x10], [0x0068, 0x10], [0x0069, 0x10], [0x0067, 0x10] /* <20> */, [0x0068, 0x10] /* <20> */, [0x0069, 0x10] /* <20> */,
[0x006A, 0x10], [0x006B, 0x10], [0x006C, 0x10], [0x006A, 0x10] /* <20> */, [0x006B, 0x10] /* <20> */, [0x006C, 0x10] /* <20> */,
[0x006D, 0x10], [0x006E, 0x10], [0x006F, 0x10], [0x006D, 0x10] /* <20> */, [0x006E, 0x10] /* <20> */, [0x006F, 0x10] /* <20> */,
[0x0070, 0x10], [0x0071, 0x10], [0x0072, 0x10], [0x0070, 0x10] /* <20> */, [0x0071, 0x10] /* <20> */, [0x0072, 0x10] /* <20> */,
[0x0073, 0x10], [0x0074, 0x10], [0x0075, 0x10], [0x0073, 0x10] /* <20> */, [0x0074, 0x10] /* <20> */, [0x0075, 0x10] /* <20> */,
[0x0076, 0x10], [0x0077, 0x10], [0x0078, 0x10], [0x0076, 0x10] /* <20> */, [0x0077, 0x10] /* <20> */, [0x0078, 0x10] /* <20> */,
[0x0079, 0x10], [0x007A, 0x10], [0x007B, 0x10], [0x0079, 0x10] /* <20> */, [0x007A, 0x10] /* <20> */, [0x007B, 0x10] /* <20> */,
[0x007C, 0x10], [0x007D, 0x10], [0x007E, 0x10], [0x007C, 0x10] /* <20> */, [0x007D, 0x10] /* <20> */, [0x007E, 0x10] /* <20> */,
[0x007F, 0x10], [0x0080, 0x10], [0x0081, 0x10], [0x007F, 0x10] /* <20> */, [0x0080, 0x10] /* <20> */, [0x0081, 0x10] /* <20> */,
[0x0082, 0x10], [0x0083, 0x10], [0x0084, 0x10], [0x0082, 0x10] /* <20> */, [0x0083, 0x10] /* <20> */, [0x0084, 0x10] /* <20> */,
[0x0085, 0x10], [0x0086, 0x10], [0x0087, 0x10], [0x0085, 0x10] /* <20> */, [0x0086, 0x10] /* <20> */, [0x0087, 0x10] /* <20> */,
[0x0088, 0x10], [0x0089, 0x10], [0x008A, 0x10], [0x0088, 0x10] /* <20> */, [0x0089, 0x10] /* <20> */, [0x008A, 0x10] /* <20> */,
[0x008B, 0x10], [0x008C, 0x10], [0x008D, 0x10], [0x008B, 0x10] /* <20> */, [0x008C, 0x10] /* <20> */, [0x008D, 0x10] /* <20> */,
[0x00AC, 0x0F], [0x008E, 0x10], [0x008F, 0x10], [0x00AC, 0x0F] /* <20> */, [0x008E, 0x10] /* <20> */, [0x008F, 0x10] /* <20> */,
[0x0090, 0x10], [0x0091, 0x10], [0x0092, 0x10], [0x0090, 0x10] /* <20> */, [0x0091, 0x10] /* <20> */, [0x0092, 0x10] /* <20> */,
[0x0093, 0x10], [0x00AE, 0x0F], [0x0094, 0x10], [0x0093, 0x10] /* <20> */, [0x00AE, 0x0F] /* <20> */, [0x0094, 0x10] /* <20> */,
[0x0095, 0x10], [0x0096, 0x10], [0x0097, 0x10], [0x0095, 0x10] /* <20> */, [0x0096, 0x10] /* <20> */, [0x0097, 0x10] /* <20> */,
[0x0098, 0x10], [0x0099, 0x10] [0x0098, 0x10] /* <20> */, [0x0099, 0x10]
]; ];
/**
* This is the table that reduces the size based on repeated patterns in the file.
*
* When we find a byte match in the ring buffer we use this table to encode the length of the matched bytes.
*
* - These are intentionally 32-bit. The leftmost flag bit is 1 in each of these to tell the decoder to use match decoding.
* - LZP hash bits are used to encode the position where the matched bytes start.
* - We're allowed to match up to 298 bytes before we can't encode more (we need an entry in this table for each byte more).
* - We can reach for matches 65KB behind the current LZ cursor (65KB is the ring buffer size and highest a 16-bit hash can reach).
**/
matchEncode = [ matchEncode = [
/* [MATCH CODE, MATCH CODE BIT LENGTH] */
[0x80000000, 0x01], [0x80000000, 0x03], [0x80000000, 0x01], [0x80000000, 0x03],
[0xA0000000, 0x03], [0xC0000000, 0x03], [0xA0000000, 0x03], [0xC0000000, 0x03],
[0xE0000000, 0x06], [0xE4000000, 0x06], [0xE0000000, 0x06], [0xE4000000, 0x06],
@@ -262,6 +298,7 @@ class WTVLzpf {
[0xFFFF4000, 0x13], [0xFFFF6000, 0x13], [0xFFFF4000, 0x13], [0xFFFF6000, 0x13],
[0xFFFF8000, 0x13], [0xFFFFA000, 0x13], [0xFFFF8000, 0x13], [0xFFFFA000, 0x13],
[0xFFFFC000, 0x13], [0xFFFFE000, 0x13], [0xFFFFC000, 0x13], [0xFFFFE000, 0x13],
// We never should select these. These were in the original executable so including them here.
[0x00000000, 0x00], [0x00000000, 0x00] [0x00000000, 0x00], [0x00000000, 0x00]
]; ];
@@ -271,7 +308,7 @@ class WTVLzpf {
* @returns {undefined} * @returns {undefined}
*/ */
constructor() { constructor() {
this.clear(); this.reset();
} }
/** /**
@@ -279,16 +316,16 @@ class WTVLzpf {
* *
* @returns {undefined} * @returns {undefined}
*/ */
clear() { reset() {
this.current_length = 0; this.current_bit_length = 0;
this.current_literal = 0; this.current_bits = 0;
this.flag = 0xFFFF; this.ring_bufer_index = 0xFFFF;
this.working_data = 0; this.working_data = 0;
this.match_index = 0; this.match_index = 0;
this.type_index = 0; this.compression_mode = 0;
this.checksum = 0; this.checksum = 0;
this.ring_buffer.fill(0x00, 0, 0x2000) this.ring_buffer.fill(this.filler_byte, 0, 0x2000)
this.flag_table.fill(0xFFFF, 0, 0x1000); this.hash_table.fill(0xFFFF, 0, 0x1000);
this.encoded_data = []; this.encoded_data = [];
} }
@@ -304,24 +341,24 @@ class WTVLzpf {
} }
/** /**
* Encodes a literal onto the compressed byte array. * Add bits onto the compressed bit stream.
* *
* @param code_length {Number} bit length of the code * When we reach 8 bits we push a byte onto the compressed byte array.
* @param code {Number} code to be encoded *
* @param bits {Number} bits to add
* @param bit_length {Number} bit length
* *
* @returns {undefined} * @returns {undefined}
*/ */
EncodeLiteral(code_length, code) { AddBits(bits, bit_length) {
// Using >>> to stick with unsigned integers without making a mess with casting. this.current_bits |= bits >>> (this.current_bit_length & 0x1F);
this.current_bit_length += bit_length;
this.current_literal |= code >>> (this.current_length & 0x1F); while (this.current_bit_length > 7) {
this.current_length += code_length; this.AddByte((this.current_bits >>> 0x18) & 0xFF);
while (this.current_length > 7) { this.current_bit_length -= 8;
this.AddByte((this.current_literal >>> 0x18) & 0xFF); this.current_bits = (this.current_bits << 8) & 0xFFFFFFFF;
this.current_length -= 8;
this.current_literal = (this.current_literal << 8) & 0xFFFFFFFF;
} }
} }
@@ -331,7 +368,7 @@ class WTVLzpf {
* @returns {undefined} Lzpf compression data * @returns {undefined} Lzpf compression data
*/ */
Begin() { Begin() {
this.clear(); this.reset();
} }
/** /**
@@ -347,7 +384,7 @@ class WTVLzpf {
var uncompressed_len = unencoded_data.byteLength; var uncompressed_len = unencoded_data.byteLength;
var i = 0; var i = 0;
var flags_index = 0; var hash_index = 0;
while (i < uncompressed_len) { while (i < uncompressed_len) {
var code_length = -1; var code_length = -1;
var code = -1; var code = -1;
@@ -356,48 +393,58 @@ class WTVLzpf {
this.ring_buffer[i & 0x1FFF] = byte; this.ring_buffer[i & 0x1FFF] = byte;
if (this.match_index > 0) { if (this.match_index > 0) {
if (byte != this.ring_buffer[this.flag] || this.match_index > 0x0127) { // Cozy time
if (byte != this.ring_buffer[this.ring_bufer_index] || this.match_index > 0x0127) {
// End of matching. Either we no longer match or we reached out limit.
code_length = this.matchEncode[this.match_index][1]; code_length = this.matchEncode[this.match_index][1];
code = this.matchEncode[this.match_index][0]; code = this.matchEncode[this.match_index][0];
this.match_index = 0; this.match_index = 0;
this.type_index = 3; this.compression_mode = 3;
} else { } else {
// Previous iteration found a match so we continue matching until we can't.
this.match_index = (this.match_index + 1) & 0x1FFF; this.match_index = (this.match_index + 1) & 0x1FFF;
this.flag = (this.flag + 1) & 0x1FFF; this.ring_bufer_index = (this.ring_bufer_index + 1) & 0x1FFF;
this.checksum = (this.checksum + byte) & 0xFFFF; this.checksum = (this.checksum + byte) & 0xFFFF;
this.working_data = ((this.working_data * 0x0100) + byte) & 0xFFFFFFFF; this.working_data = ((this.working_data * 0x0100) + byte) & 0xFFFFFFFF;
i++; i++;
} }
} else { } else {
this.flag = 0xFFFF; this.ring_bufer_index = 0xFFFF;
if (i >= 3) { if (i >= 3) {
flags_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF; // Start recoding data so we can lookup matches.
this.flag = this.flag_table[flags_index]; hash_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF;
this.flag_table[flags_index] = i & 0x1FFF; this.ring_bufer_index = this.hash_table[hash_index];
this.hash_table[hash_index] = i & 0x1FFF;
} else { } else {
this.type_index++; // The first three uncompressed bytes aren't used for the matching algorithm.
this.compression_mode++;
} }
if (this.flag == 0xFFFF) { if (this.ring_bufer_index == 0xFFFF) {
// We never seen this byte before so we encode it with our Huffman table.
code_length = this.nomatchEncode[byte][1]; code_length = this.nomatchEncode[byte][1];
code = this.nomatchEncode[byte][0] << 0x10; code = this.nomatchEncode[byte][0] << 0x10;
} else if (byte == this.ring_buffer[this.flag] && compress_data) { } else if (byte == this.ring_buffer[this.ring_bufer_index] && compress_data) {
// Wow dude, a match has been found. Let's switch get our own room in the next iteration to see if we match further.
this.match_index = 1; this.match_index = 1;
this.flag = (this.flag + 1) & 0x1FFF; this.ring_bufer_index = (this.ring_bufer_index + 1) & 0x1FFF;
this.type_index = 4; this.compression_mode = 4;
} else { } else {
// We've seen these bytes before but the index in the ring buffer doesn't match so we revert to our neat Huffman table
// We add 1 flag bit of 0 to account for the fact we've had a hash table hit but no hit in the ring buffer.
code_length = this.nomatchEncode[byte][1] + 1; code_length = this.nomatchEncode[byte][1] + 1;
code = this.nomatchEncode[byte][0] << 0x0F; code = this.nomatchEncode[byte][0] << 0x0F;
} }
this.checksum = (this.checksum + byte) & 0xFFFF; this.checksum = (this.checksum + byte) & 0xFFFF;
// We work on a 2-byte context so we store the last two bytes so we can do cool lookups with it
this.working_data = ((this.working_data * 0x0100) + byte) & 0xFFFFFFFF; this.working_data = ((this.working_data * 0x0100) + byte) & 0xFFFFFFFF;
i++; i++;
} }
if (code_length > 0) { if (code_length > 0) {
this.EncodeLiteral(code_length, code); this.AddBits(code, code_length);
} }
} }
} }
@@ -405,7 +452,7 @@ class WTVLzpf {
/** /**
* Ends a compression stream. * Ends a compression stream.
* *
* @param type_index {Number} the end type used to finalize * @param compression_mode {Number} the end type used to finalize
* *
* @returns {Buffer} Lzpf compression data * @returns {Buffer} Lzpf compression data
*/ */
@@ -413,33 +460,34 @@ class WTVLzpf {
var code_length = -1; var code_length = -1;
var code = -1; var code = -1;
if (this.type_index == 2) { if (this.compression_mode == 2) {
this.EncodeLiteral(0x10, 0x00990000); this.AddBits(0x00990000, 0x10);
} else if (this.type_index >= 3) { } else if (this.compression_mode >= 3) {
if (this.type_index == 4) { if (this.compression_mode == 4) {
code_length = this.matchEncode[this.match_index][1]; code_length = this.matchEncode[this.match_index][1];
code = this.matchEncode[this.match_index][0]; code = this.matchEncode[this.match_index][0];
this.EncodeLiteral(code_length, code); this.AddBits(code, code_length);
} }
var flags_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF; var hash_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF;
var flag = this.flag_table[flags_index]; var ring_bufer_index = this.hash_table[hash_index];
if (flag == 0xFFFF) { if (ring_bufer_index == 0xFFFF) {
this.EncodeLiteral(0x10, 0x00990000); this.AddBits(0x00990000, 0x10);
} else { } else {
this.EncodeLiteral(0x11, 0x004c8000); this.AddBits(0x004C8000, 0x11);
} }
} }
// Below is just metadata. The compressed block is complete. // Add checksum bits
this.AddBits((this.checksum << 0x10) & 0xFFFFFFFF, 0x08);
this.AddBits((this.checksum << 0x18) & 0xFFFFFFFF, 0x08);
// Encode checksum // If we have leftover bits then add it.
this.EncodeLiteral(0x08, (this.checksum << 0x10) & 0xFFFFFFFF); if (this.current_bit_length > 0) {
this.EncodeLiteral(0x08, (this.checksum << 0x18) & 0xFFFFFFFF); this.AddByte((this.current_bits >>> 0x18) & 0xFF);
}
// End this.AddByte(this.filler_byte);
this.AddByte((this.current_literal >>> 0x18) & 0xFF);
this.AddByte(0x20);
return Buffer.from(this.encoded_data); return Buffer.from(this.encoded_data);
} }

View File

@@ -75,6 +75,7 @@ class WTVMime {
// gzip only // gzip only
if (content_type.match(/^audio\/(x-)?(s3m|mod|xm)$/)) compress_data = true; // s3m, mod, xm if (content_type.match(/^audio\/(x-)?(s3m|mod|xm)$/)) compress_data = true; // s3m, mod, xm
if (content_type.match(/^audio\/(x-)?(midi|wav|wave|aif(f)?)$/)) compress_data = true; // midi & wav if (content_type.match(/^audio\/(x-)?(midi|wav|wave|aif(f)?)$/)) compress_data = true; // midi & wav
if (content_type.match(/^application\/karaoke$/)) compress_data = true; // midi karaoke
if (content_type.match(/^binary\/x-wtv-approm$/)) compress_data = true; // approms if (content_type.match(/^binary\/x-wtv-approm$/)) compress_data = true; // approms
if (content_type.match(/^binary\/doom-data$/)) compress_data = true; // DOOM WADs if (content_type.match(/^binary\/doom-data$/)) compress_data = true; // DOOM WADs
if (content_type.match(/^wtv\/download-list$/)) compress_data = true; // WebTV Download List if (content_type.match(/^wtv\/download-list$/)) compress_data = true; // WebTV Download List
@@ -179,6 +180,9 @@ class WTVMime {
case "wad": case "wad":
wtv_mime_type = "binary/doom-data"; wtv_mime_type = "binary/doom-data";
break; break;
case "kar":
wtv_mime_type = "application/karaoke";
break;
case "mp2": case "mp2":
case "hsb": case "hsb":
case "rmf": case "rmf":
@@ -190,6 +194,7 @@ class WTVMime {
} }
modern_mime_type = this.mime.lookup(path); modern_mime_type = this.mime.lookup(path);
if (modern_mime_type === false) modern_mime_type = "application/octet-stream";
if (wtv_mime_type == "") wtv_mime_type = modern_mime_type; if (wtv_mime_type == "") wtv_mime_type = modern_mime_type;
return new Array(wtv_mime_type, modern_mime_type); return new Array(wtv_mime_type, modern_mime_type);
} }

View File

@@ -199,6 +199,68 @@ class WTVShared {
return path.reverse().split(".")[0].reverse(); return path.reverse().split(".")[0].reverse();
} }
getLineFromFile(filename, line_no, callback) {
var stream = this.fs.createReadStream(filename, {
flags: 'r',
encoding: 'utf-8',
fd: null,
bufferSize: 64 * 1024
});
var fileData = '';
stream.on('data', function (data) {
fileData += data;
// The next lines should be improved
var lines = fileData.split("\n");
if (lines.length >= +line_no) {
stream.destroy();
callback(null, lines[+line_no]);
}
});
stream.on('error', function () {
callback('Error', null);
});
stream.on('end', function () {
callback('File end reached without finding line', null);
});
}
doErrorPage(code, data = null, pc_mode = false) {
var headers = null;
switch (code) {
case 404:
if (data === null) data = "The service could not find the requested page.";
if (pc_mode) headers = "404 Not Found\n";
else headers = code + " " + data + "\n";
headers += "Content-Type: text/html\n";
break;
case 400:
case 500:
if (data === null) data = "HackTV ran into a technical problem.";
if (pc_mode) headers = "500 Internal Server Error\n";
else headers = code + " " + data + "\n";
headers += "Content-Type: text/html\n";
break;
case 401:
if (data === null) data = "Access Denied.";
if (pc_mode) headers = "401 Access Denied\n";
else headers = code + " " + data + "\n";
headers += "Content-Type: text/html\n";
break;
default:
headers = code + " " + data + "\n";
headers += "Content-Type: text/html\n";
break;
}
console.error(" * doErrorPage Called:", code, data);
return new Array(headers, data);
}
/** /**
* Strips bad things from paths * Strips bad things from paths
* @param {string} base Base path * @param {string} base Base path

View File

@@ -71,47 +71,31 @@ function getServiceString(service, overrides = {}) {
} }
} }
// passthrough for old scripts
function doErrorPage(code, data = null, pc_mode = false) { function doErrorPage(code, data = null, pc_mode = false) {
var headers = null; return wtvshared.doErrorPage(code, data, pc_mode);
switch (code) { }
case 404:
if (data === null) data = "The service could not find the requested page."; async function sendRawFile(socket, path) {
if (pc_mode) headers = "404 Not Found\n"; if (!minisrv_config.config.debug_flags.quiet) console.log(" * Found " + path + " to handle request (Direct File Mode) [Socket " + socket.id + "]");
else headers = code + " "+ data + "\n"; var contypes = wtvmime.getContentType(path);
headers += "Content-Type: text/html\n"; var headers = "200 OK\n"
break; headers += "Content-Type: " + contypes[0] + "\n";
case 400: headers += "wtv-modern-content-type" + contypes[1];
case 500: fs.readFile(path, null, function (err, data) {
if (data === null) data = "HackTV ran into a technical problem."; sendToClient(socket, headers, data);
if (pc_mode) headers = "500 Internal Server Error\n"; });
else headers = code + " " + data + "\n";
headers += "Content-Type: text/html\n";
break;
case 401:
if (data === null) data = "Access Denied.";
if (pc_mode) headers = "401 Access Denied\n";
else headers = code + " " + data + "\n";
headers += "Content-Type: text/html\n";
break;
default:
headers = code + " " + data + "\n";
headers += "Content-Type: text/html\n";
break;
}
console.error(" * doErrorPage Called:", code, data);
return new Array(headers, data);
} }
async function processPath(socket, service_vault_file_path, request_headers = new Array(), service_name) { async function processPath(socket, service_vault_file_path, request_headers = new Array(), service_name) {
var headers, data = null; var headers, data = null;
var request_is_async = false; var request_is_async = false;
var service_vault_found = false; var service_vault_found = false;
var service_path = service_vault_file_path; var service_path = unescape(service_vault_file_path);
try { try {
service_vaults.forEach(function (service_vault_dir) { service_vaults.forEach(function (service_vault_dir) {
if (service_vault_found) return; if (service_vault_found) return;
service_vault_file_path = wtvshared.makeSafePath(service_vault_dir, service_path); service_vault_file_path = wtvshared.makeSafePath(service_vault_dir, service_path);
// deny access to catchall file name directly // deny access to catchall file name directly
var service_path_split = service_path.split("/"); var service_path_split = service_path.split("/");
var service_path_request_file = service_path_split[service_path_split.length - 1]; var service_path_request_file = service_path_split[service_path_split.length - 1];
@@ -121,28 +105,66 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
else minisrv_catchall = minisrv_config.config.catchall_file_name || null; else minisrv_catchall = minisrv_config.config.catchall_file_name || null;
if (minisrv_catchall) { if (minisrv_catchall) {
if (service_path_request_file == minisrv_catchall) { if (service_path_request_file == minisrv_catchall) {
var errpage = doErrorPage(401, "Access Denied"); request_is_async = true;
headers = errpage[0]; var errpage = wtvshared.doErrorPage(401, "Access Denied");
data = errpage[1]; sendToClient(socket, errpage[0], errpage[1]);
return; return;
} }
} }
} }
var is_dir = false;
var file_exists = false;
minisrv_catchall, service_path_split, service_path_request_file = null; minisrv_catchall, service_path_split, service_path_request_file = null;
if (fs.existsSync(service_vault_file_path)) { if (fs.existsSync(service_vault_file_path)) {
file_exists = true;
is_dir = fs.lstatSync(service_vault_file_path).isDirectory()
}
if (file_exists && !is_dir) {
// file exists, read it and return it // file exists, read it and return it
service_vault_found = true; service_vault_found = true;
request_is_async = true; request_is_async = true;
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Found " + service_vault_file_path + " to handle request (Direct File Mode) [Socket " + socket.id + "]");
request_headers.service_file_path = service_vault_file_path; request_headers.service_file_path = service_vault_file_path;
var contypes = wtvmime.getContentType(service_vault_file_path); request_headers.raw_file = true;
headers = "200 OK\n"
headers += "Content-Type: " + contypes[0] + "\n"; // service parsed files, we might not want to expose our service source files so we can protect them with a flag on the first line
headers += "wtv-modern-content-type" + contypes[1]; if (wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "js" || wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "txt") {
fs.readFile(service_vault_file_path, null, function (err, data) { if (wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "js") {
sendToClient(socket, headers, data); wtvshared.getLineFromFile(service_vault_file_path, 0, function (status, line) {
if (!status) {
if (line.match(/minisrv\_service\_file.*true/i)) {
var errpage = wtvshared.doErrorPage(403, "Access Denied");
sendToClient(socket, errpage[0], errpage[1]);
} else {
sendRawFile(socket, service_vault_file_path);
}
} else {
var errpage = wtvshared.doErrorPage(400);
sendToClient(socket, errpage[0], errpage[1]);
}
}); });
}
if (wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "txt") {
wtvshared.getLineFromFile(service_vault_file_path, 0, function (status, line) {
if (!status) {
if (line.match(/^#!minisrv/i)) {
var errpage = wtvshared.doErrorPage(403, "Access Denied");
sendToClient(socket, errpage[0], errpage[1]);
} else {
sendRawFile(socket, service_vault_file_path);
}
} else {
var errpage = wtvshared.doErrorPage(400);
sendToClient(socket, errpage[0], errpage[1]);
}
});
}
} else {
// not a potential service file, so save to send
sendRawFile(socket, service_vault_file_path);
}
} else if (fs.existsSync(service_vault_file_path + ".txt")) { } else if (fs.existsSync(service_vault_file_path + ".txt")) {
// raw text format, entire payload expected (headers and content) // raw text format, entire payload expected (headers and content)
service_vault_found = true; service_vault_found = true;
@@ -211,6 +233,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
while (service_check_dir.join(path.sep) != service_vault_dir) { while (service_check_dir.join(path.sep) != service_vault_dir) {
var catchall_file = service_check_dir.join(path.sep) + path.sep + minisrv_catchall_file_name; var catchall_file = service_check_dir.join(path.sep) + path.sep + minisrv_catchall_file_name;
if (fs.existsSync(catchall_file)) { if (fs.existsSync(catchall_file)) {
service_vault_found = true;
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Found catchall at " + catchall_file + " to handle request (JS Interpreter Mode) [Socket " + socket.id + "]"); if (!minisrv_config.config.debug_flags.quiet) console.log(" * Found catchall at " + catchall_file + " to handle request (JS Interpreter Mode) [Socket " + socket.id + "]");
request_headers.service_file_path = catchall_file; request_headers.service_file_path = catchall_file;
var jscript_eval = fs.readFileSync(catchall_file).toString(); var jscript_eval = fs.readFileSync(catchall_file).toString();
@@ -228,7 +251,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
// either `request_is_async`, or `headers` and `data` MUST be defined by this point! // either `request_is_async`, or `headers` and `data` MUST be defined by this point!
}); });
} catch (e) { } catch (e) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1] + "<br><br>The interpreter said:<br><pre>" + e.toString() + "</pre>"; data = errpage[1] + "<br><br>The interpreter said:<br><pre>" + e.toString() + "</pre>";
console.error(" * Scripting error:",e); console.error(" * Scripting error:",e);
@@ -236,12 +259,12 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
if (!request_is_async) { if (!request_is_async) {
if (!service_vault_found) { if (!service_vault_found) {
console.error(" * Could not find a Service Vault for " + service_name + ":/" + service_path.replace(service_name + path.sep, "")); console.error(" * Could not find a Service Vault for " + service_name + ":/" + service_path.replace(service_name + path.sep, ""));
var errpage = doErrorPage(404, null, socket.minisrv_pc_mode); var errpage = wtvshared.doErrorPage(404, null, socket.minisrv_pc_mode);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
} }
if (headers == null && !request_is_async) { if (headers == null && !request_is_async) {
var errpage = doErrorPage(400, null, socket.minisrv_pc_mode); var errpage = wtvshared.doErrorPage(400, null, socket.minisrv_pc_mode);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
console.error(" * Scripting or Data error: Headers were not defined. (headers,data) as follows:") console.error(" * Scripting or Data error: Headers were not defined. (headers,data) as follows:")
@@ -316,7 +339,7 @@ async function processURL(socket, request_headers) {
shortURL_split.shift(); shortURL_split.shift();
var shortURL_service_path = shortURL_split.join(":"); var shortURL_service_path = shortURL_split.join(":");
shortURL = shortURL_service_name + ":/" + shortURL_service_path; shortURL = shortURL_service_name + ":/" + shortURL_service_path;
} else if (shortURL.indexOf(":") == -1 && request_headers.request.indexOf("HTTP/1") > 0) { } else if (shortURL.indexOf(":") == -1 && request_headers.request.indexOf("HTTP/1") > 0 && socket.ssid == null) {
if (request_headers.Host) { if (request_headers.Host) {
if (minisrv_config.config.pc_server_hidden_service_enabled) { if (minisrv_config.config.pc_server_hidden_service_enabled) {
// browsers typically send a Host header // browsers typically send a Host header
@@ -329,7 +352,7 @@ async function processURL(socket, request_headers) {
} else { } else {
// minimal pc mode to send error // minimal pc mode to send error
socket.minisrv_pc_mode = true; socket.minisrv_pc_mode = true;
var errpage = doErrorPage(401, "PC services are disabled on this server", socket.minisrv_pc_mode); var errpage = wtvshared.doErrorPage(401, "PC services are disabled on this server", socket.minisrv_pc_mode);
headers = errpage[0]; headers = errpage[0];
data = errpage[1] data = errpage[1]
socket_sessions[socket.id].close_me = true; socket_sessions[socket.id].close_me = true;
@@ -356,7 +379,7 @@ async function processURL(socket, request_headers) {
} }
// assume webtv since there is a :/ in the GET // assume webtv since there is a :/ in the GET
var service_name = shortURL.split(':/')[0]; var service_name = shortURL.split(':/')[0];
var urlToPath = service_name + path.sep + shortURL.split(':/')[1]; var urlToPath = wtvshared.fixPathSlashes(service_name + path.sep + shortURL.split(':/')[1]);
if (minisrv_config.config.debug_flags.show_headers) console.log(" * Incoming headers on socket ID", socket.id, (await wtvshared.filterSSID(request_headers))); if (minisrv_config.config.debug_flags.show_headers) console.log(" * Incoming headers on socket ID", socket.id, (await wtvshared.filterSSID(request_headers)));
socket_sessions[socket.id].request_headers = request_headers; socket_sessions[socket.id].request_headers = request_headers;
processPath(socket, urlToPath, request_headers, service_name); processPath(socket, urlToPath, request_headers, service_name);
@@ -364,7 +387,7 @@ async function processURL(socket, request_headers) {
doHTTPProxy(socket, request_headers); doHTTPProxy(socket, request_headers);
} else { } else {
// error reading headers (no request_url provided) // error reading headers (no request_url provided)
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1] data = errpage[1]
socket_sessions[socket.id].close_me = true; socket_sessions[socket.id].close_me = true;
@@ -465,11 +488,11 @@ async function doHTTPProxy(socket, request_headers) {
}); });
}).on('error', function (err) { }).on('error', function (err) {
var errpage, headers, data = null; var errpage, headers, data = null;
if (err.code == "ENOTFOUND") errpage = doErrorPage(400, `The publisher ${request_data.host} is unknown.`); if (err.code == "ENOTFOUND") errpage = wtvshared.doErrorPage(400, `The publisher ${request_data.host} is unknown.`);
else if (err.message.indexOf("HostUnreachable") > 0) errpage = doErrorPage(400, `The publisher ${request_data.host} could not be reached.`); else if (err.message.indexOf("HostUnreachable") > 0) errpage = wtvshared.doErrorPage(400, `The publisher ${request_data.host} could not be reached.`);
else { else {
console.log(" * Unhandled Proxy Request Error:", err); console.log(" * Unhandled Proxy Request Error:", err);
errpage = doErrorPage(400); errpage = wtvshared.doErrorPage(400);
} }
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
@@ -521,7 +544,18 @@ function headerStringToObj(headers, response = false) {
headers_obj.http_response = d.replace("\r", ""); headers_obj.http_response = d.replace("\r", "");
} else if (/^(GET |PUT |POST)$/.test(d.substring(0, 4)) && !response) { } else if (/^(GET |PUT |POST)$/.test(d.substring(0, 4)) && !response) {
headers_obj.request = d.replace("\r", ""); headers_obj.request = d.replace("\r", "");
headers_obj.request_url = decodeURI(d.split(' ')[1]).replace("\r", ""); var request_url = d.split(' ');
if (request_url.length > 2) {
request_url.shift();
request_url = request_url.join(" ");
if (request_url.indexOf("HTTP/") > 0) {
var index = request_url.indexOf(" HTTP/");
request_url = request_url.substring(0, index);
}
} else {
request_url = request_url[1];
}
headers_obj.request_url = decodeURI(request_url).replace("\r", "");
} else if (d.indexOf(":") > 0) { } else if (d.indexOf(":") > 0) {
var d_split = d.split(':'); var d_split = d.split(':');
var header_name = d_split[0]; var header_name = d_split[0];
@@ -578,7 +612,7 @@ async function sendToClient(socket, headers_obj, data) {
if (socket_sessions[socket.id]) { if (socket_sessions[socket.id]) {
if (socket_sessions[socket.id].request_headers) { if (socket_sessions[socket.id].request_headers) {
if (socket_sessions[socket.id].request_headers.service_file_path) { if (socket_sessions[socket.id].request_headers.service_file_path) {
if (wtvshared.getFileExt(socket_sessions[socket.id].request_headers.service_file_path).toLowerCase() !== "js") { if (wtvshared.getFileExt(socket_sessions[socket.id].request_headers.service_file_path).toLowerCase() !== "js" || socket_sessions[socket.id].request_headers.raw_file === true) {
var last_modified = wtvshared.getFileLastModifiedUTCString(socket_sessions[socket.id].request_headers.service_file_path); var last_modified = wtvshared.getFileLastModifiedUTCString(socket_sessions[socket.id].request_headers.service_file_path);
if (last_modified) headers_obj["Last-Modified"] = last_modified; if (last_modified) headers_obj["Last-Modified"] = last_modified;
} }
@@ -836,7 +870,7 @@ function checkSecurity(socket) {
if (blacklist) console.log(" * Request from SSID", wtvshared.filterSSID(ssid), "(" + socket.remoteAddr + "), but that SSID is in the blacklist, rejecting."); if (blacklist) console.log(" * Request from SSID", wtvshared.filterSSID(ssid), "(" + socket.remoteAddr + "), but that SSID is in the blacklist, rejecting.");
else console.log(" * Request from SSID", wtvshared.filterSSID(socket.ssid), "(" + socket.remoteAddress + "), but that SSID is not in the whitelist, rejecting."); else console.log(" * Request from SSID", wtvshared.filterSSID(socket.ssid), "(" + socket.remoteAddress + "), but that SSID is not in the whitelist, rejecting.");
var errpage = doErrorPage(401, "Access to this service is denied."); var errpage = wtvshared.doErrorPage(401, "Access to this service is denied.");
out = errpage; out = errpage;
} }
@@ -942,7 +976,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2));
if (enc_data.sigBytes > 0) { if (enc_data.sigBytes > 0) {
if (!socket_sessions[socket.id].wtvsec) { if (!socket_sessions[socket.id].wtvsec) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
headers += "wtv-visit: client:relog\n"; headers += "wtv-visit: client:relog\n";
data = errpage[1]; data = errpage[1];
@@ -1010,7 +1044,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
if (!ssid_sessions[socket.ssid].data_store.wtvsec_login) { if (!ssid_sessions[socket.ssid].data_store.wtvsec_login) {
ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(minisrv_config); ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(minisrv_config);
ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge(); ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge();
ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(headers["wtv-incarnation"]); if (headers["wtv-incarnation"]) ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(headers["wtv-incarnation"]);
ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = headers["wtv-ticket"]; ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = headers["wtv-ticket"];
ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64); ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64);
} else { } else {
@@ -1018,7 +1052,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
if (minisrv_config.config.debug_flags.debug) console.log(" # New ticket from client"); if (minisrv_config.config.debug_flags.debug) console.log(" # New ticket from client");
ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = headers["wtv-ticket"]; ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = headers["wtv-ticket"];
ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64); ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64);
ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(headers["wtv-incarnation"]); if (headers["wtv-incarnation"]) ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(headers["wtv-incarnation"]);
} }
} }
} }
@@ -1077,7 +1111,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
if (minisrv_config.config.debug_flags.show_headers) console.log(secure_headers); if (minisrv_config.config.debug_flags.show_headers) console.log(secure_headers);
if (!secure_headers.request) { if (!secure_headers.request) {
socket_sessions[socket.id].secure = false; socket_sessions[socket.id].secure = false;
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
sendToClient(socket, headers, data); sendToClient(socket, headers, data);
@@ -1138,7 +1172,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
processURL(socket, headers); processURL(socket, headers);
} else if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) { } else if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) {
// got too much data ? ... should not ever reach this code (section 2) // got too much data ? ... should not ever reach this code (section 2)
var errpage = doErrorPage(400, "Received too much data in POST request<br>Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length) + " (2)"; var errpage = wtvshared.doErrorPage(400, "Received too much data in POST request<br>Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length) + " (2)";
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
sendToClient(socket, headers, data); sendToClient(socket, headers, data);
@@ -1220,7 +1254,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data; if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data;
socket.setTimeout(minisrv_config.config.socket_timeout * 1000); socket.setTimeout(minisrv_config.config.socket_timeout * 1000);
// got too much data ? ... should not ever reach this code // got too much data ? ... should not ever reach this code
var errpage = doErrorPage(400, "Received too much data in POST request<br>Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length); var errpage = wtvshared.doErrorPage(400, "Received too much data in POST request<br>Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length);
headers = errpage[0]; headers = errpage[0];
data = errpage[1]; data = errpage[1];
sendToClient(socket, headers, data); sendToClient(socket, headers, data);
@@ -1238,7 +1272,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
var enc_data = CryptoJS.enc.Hex.parse(data_hex); var enc_data = CryptoJS.enc.Hex.parse(data_hex);
if (enc_data.sigBytes > 0) { if (enc_data.sigBytes > 0) {
if (!socket_sessions[socket.id].wtvsec) { if (!socket_sessions[socket.id].wtvsec) {
var errpage = doErrorPage(400); var errpage = wtvshared.doErrorPage(400);
var headers = errpage[0]; var headers = errpage[0];
headers += "wtv-visit: client:relog\n"; headers += "wtv-visit: client:relog\n";
data = errpage[1]; data = errpage[1];
@@ -1330,6 +1364,7 @@ async function cleanupSocket(socket) {
async function handleSocket(socket) { async function handleSocket(socket) {
// create unique socket id with client address and port // create unique socket id with client address and port
socket.id = parseInt(crc16('CCITT-FALSE', Buffer.from(String(socket.remoteAddress) + String(socket.remotePort), "utf8")).toString(16), 16); socket.id = parseInt(crc16('CCITT-FALSE', Buffer.from(String(socket.remoteAddress) + String(socket.remotePort), "utf8")).toString(16), 16);
socket.ssid = null;
socket_sessions[socket.id] = []; socket_sessions[socket.id] = [];
socket.minisrv_pc_mode = false; socket.minisrv_pc_mode = false;
socket.setEncoding('hex'); //set data encoding (Text: 'ascii', 'utf8' ~ Binary: 'hex', 'base64' (do not trust 'binary' encoding)) socket.setEncoding('hex'); //set data encoding (Text: 'ascii', 'utf8' ~ Binary: 'hex', 'base64' (do not trust 'binary' encoding))

View File

@@ -1,6 +1,6 @@
{ {
"config": { "config": {
"service_ip": "0.0.0.0", "service_ip": "127.0.0.1",
"ServiceVaults": [ "ServiceVaults": [
"UserServiceVault", "UserServiceVault",
"ServiceVault" "ServiceVault"
@@ -17,7 +17,7 @@
"post_data_socket_timeout": 30, "post_data_socket_timeout": 30,
"error_log_file": "errors.log", "error_log_file": "errors.log",
"catchall_file_name": "catchall.js", "catchall_file_name": "catchall.js",
"enable_lzpf_compression": false, "enable_lzpf_compression": true,
"enable_gzip_compression": true, "enable_gzip_compression": true,
"pc_server_hidden_service": "http_pc", "pc_server_hidden_service": "http_pc",
"pc_server_hidden_service_enabled": false, "pc_server_hidden_service_enabled": false,
@@ -59,7 +59,7 @@
"flags": "0x00000040", "flags": "0x00000040",
"debug": false, "debug": false,
"use_zefie_server": true, "use_zefie_server": true,
"bf0app_default_rom": "content/artemis-webtv-000/build7181/daily-nondebug/bf0app-part000.rom", "bf0app_default_rom": "content/artemis-webtv-000/build7588/daily-nondebug/bf0app-part000.rom",
"catchall_file_name": "content-serve.js" "catchall_file_name": "content-serve.js"
}, },
"wtv-setup": { "wtv-setup": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "zefie_wtvp_minisrv", "name": "zefie_wtvp_minisrv",
"version": "0.9.18", "version": "0.9.20",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -36,9 +36,9 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"crypto-js": { "crypto-js": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==" "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
}, },
"data-uri-to-buffer": { "data-uri-to-buffer": {
"version": "3.0.1", "version": "3.0.1",
@@ -444,9 +444,9 @@
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
}, },
"vm2": { "vm2": {
"version": "3.9.3", "version": "3.9.5",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.3.tgz", "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.5.tgz",
"integrity": "sha512-smLS+18RjXYMl9joyJxMNI9l4w7biW8ilSDaVRvFBDwOH8P0BK1ognFQTpg0wyQ6wIKLTblHJvROW692L/E53Q==" "integrity": "sha512-LuCAHZN75H9tdrAiLFf030oW7nJV5xwNMuk1ymOZwopmuK3d2H4L1Kv4+GFHgarKiLfXXLFU+7LDABHnwOkWng=="
}, },
"word-wrap": { "word-wrap": {
"version": "1.2.3", "version": "1.2.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "zefie_wtvp_minisrv", "name": "zefie_wtvp_minisrv",
"version": "0.9.19", "version": "0.9.21",
"description": "WebTV Service (WTVP) Emulation Server", "description": "WebTV Service (WTVP) Emulation Server",
"main": "app.js", "main": "app.js",
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv", "homepage": "https://github.com/zefie/zefie_wtvp_minisrv",
@@ -26,7 +26,7 @@
"url": "https://github.com/zefie/zefie_wtvp_minisrv.git" "url": "https://github.com/zefie/zefie_wtvp_minisrv.git"
}, },
"dependencies": { "dependencies": {
"crypto-js": "^4.0.0", "crypto-js": "^4.1.1",
"easy-crc": "0.0.2", "easy-crc": "0.0.2",
"endianness": "^8.0.2", "endianness": "^8.0.2",
"mime-types": "^2.1.31", "mime-types": "^2.1.31",